diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/SYNQueue.podspec b/SYNQueue.podspec old mode 100644 new mode 100755 diff --git a/SYNQueue.xcworkspace/contents.xcworkspacedata b/SYNQueue.xcworkspace/contents.xcworkspacedata old mode 100644 new mode 100755 diff --git a/SYNQueue/SYNQueue.xcodeproj/project.pbxproj b/SYNQueue/SYNQueue.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 94153bc..08cb928 --- a/SYNQueue/SYNQueue.xcodeproj/project.pbxproj +++ b/SYNQueue/SYNQueue.xcodeproj/project.pbxproj @@ -12,10 +12,10 @@ 9FD63F871B333B81001BD09A /* SYNQueueTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63F861B333B81001BD09A /* SYNQueueTask.swift */; }; 9FD63FBF1B334316001BD09A /* NSDate+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FBE1B334316001BD09A /* NSDate+Utils.swift */; }; 9FD63FC11B335579001BD09A /* SYNQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FC01B335579001BD09A /* SYNQueue.swift */; }; - DCD110DA1BBA166B003AF0F0 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110D81BBA166B003AF0F0 /* ConsoleLogger.swift */; settings = {ASSET_TAGS = (); }; }; - DCD110DB1BBA166B003AF0F0 /* NSUserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110D91BBA166B003AF0F0 /* NSUserDefaultsSerializer.swift */; settings = {ASSET_TAGS = (); }; }; + DCD110DA1BBA166B003AF0F0 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110D81BBA166B003AF0F0 /* ConsoleLogger.swift */; }; + DCD110DB1BBA166B003AF0F0 /* UserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110D91BBA166B003AF0F0 /* UserDefaultsSerializer.swift */; }; DCD110E01BBA2F1E003AF0F0 /* SYNQueue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F62022B1B333AAF0026CE2C /* SYNQueue.framework */; }; - DCD110E21BBA2FB0003AF0F0 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110E11BBA2FB0003AF0F0 /* Utils.swift */; settings = {ASSET_TAGS = (); }; }; + DCD110E21BBA2FB0003AF0F0 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110E11BBA2FB0003AF0F0 /* Utils.swift */; }; DCD110E31BBA2FBC003AF0F0 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110E11BBA2FB0003AF0F0 /* Utils.swift */; }; /* End PBXBuildFile section */ @@ -40,7 +40,7 @@ 9FD63FBE1B334316001BD09A /* NSDate+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSDate+Utils.swift"; sourceTree = ""; }; 9FD63FC01B335579001BD09A /* SYNQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SYNQueue.swift; sourceTree = ""; }; DCD110D81BBA166B003AF0F0 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = ""; }; - DCD110D91BBA166B003AF0F0 /* NSUserDefaultsSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaultsSerializer.swift; sourceTree = ""; }; + DCD110D91BBA166B003AF0F0 /* UserDefaultsSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsSerializer.swift; sourceTree = ""; }; DCD110E11BBA2FB0003AF0F0 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -115,7 +115,7 @@ isa = PBXGroup; children = ( DCD110D81BBA166B003AF0F0 /* ConsoleLogger.swift */, - DCD110D91BBA166B003AF0F0 /* NSUserDefaultsSerializer.swift */, + DCD110D91BBA166B003AF0F0 /* UserDefaultsSerializer.swift */, 9F62023C1B333AAF0026CE2C /* Info.plist */, ); name = "Supporting Files"; @@ -179,14 +179,16 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0630; + LastUpgradeCheck = 0830; ORGANIZATIONNAME = Syntertainment; TargetAttributes = { 9F62022A1B333AAF0026CE2C = { CreatedOnToolsVersion = 6.3.2; + LastSwiftMigration = 0830; }; 9F6202351B333AAF0026CE2C = { CreatedOnToolsVersion = 6.3.2; + LastSwiftMigration = 0830; }; }; }; @@ -242,7 +244,7 @@ buildActionMask = 2147483647; files = ( DCD110E31BBA2FBC003AF0F0 /* Utils.swift in Sources */, - DCD110DB1BBA166B003AF0F0 /* NSUserDefaultsSerializer.swift in Sources */, + DCD110DB1BBA166B003AF0F0 /* UserDefaultsSerializer.swift in Sources */, DCD110DA1BBA166B003AF0F0 /* ConsoleLogger.swift in Sources */, 9F62023E1B333AAF0026CE2C /* SYNQueueTests.swift in Sources */, ); @@ -272,8 +274,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -281,6 +285,7 @@ CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -320,8 +325,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -341,6 +348,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -353,6 +361,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -362,9 +371,11 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -373,6 +384,7 @@ buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -382,8 +394,10 @@ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -397,7 +411,9 @@ ); INFOPLIST_FILE = SYNQueueTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -407,7 +423,9 @@ ENABLE_TESTABILITY = NO; INFOPLIST_FILE = SYNQueueTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/SYNQueue/SYNQueue.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SYNQueue/SYNQueue.xcodeproj/project.xcworkspace/contents.xcworkspacedata old mode 100644 new mode 100755 diff --git a/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcbaselines/9F6202351B333AAF0026CE2C.xcbaseline/24523CEE-5CA3-4AAB-A970-50D68B159D1F.plist b/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcbaselines/9F6202351B333AAF0026CE2C.xcbaseline/24523CEE-5CA3-4AAB-A970-50D68B159D1F.plist old mode 100644 new mode 100755 diff --git a/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcbaselines/9F6202351B333AAF0026CE2C.xcbaseline/Info.plist b/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcbaselines/9F6202351B333AAF0026CE2C.xcbaseline/Info.plist old mode 100644 new mode 100755 diff --git a/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcschemes/SYNQueue.xcscheme b/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcschemes/SYNQueue.xcscheme old mode 100644 new mode 100755 index 629f143..a103ed1 --- a/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcschemes/SYNQueue.xcscheme +++ b/SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcschemes/SYNQueue.xcscheme @@ -1,6 +1,6 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SYNQueue/SYNQueue/NSDate+Utils.swift b/SYNQueue/SYNQueue/NSDate+Utils.swift old mode 100644 new mode 100755 index 19d7bcb..ae7ee96 --- a/SYNQueue/SYNQueue/NSDate+Utils.swift +++ b/SYNQueue/SYNQueue/NSDate+Utils.swift @@ -5,13 +5,13 @@ import Foundation -class ISOFormatter : NSDateFormatter { +class ISOFormatter : DateFormatter { override init() { super.init() self.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z" - self.timeZone = NSTimeZone(forSecondsFromGMT: 0) - self.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)! - self.locale = NSLocale(localeIdentifier: "en_US_POSIX") + self.timeZone = TimeZone(secondsFromGMT: 0) + self.calendar = Calendar(identifier: Calendar.Identifier.iso8601) + self.locale = Locale(identifier: "en_US_POSIX") } required init?(coder aDecoder: NSCoder) { @@ -19,15 +19,14 @@ class ISOFormatter : NSDateFormatter { } } -extension NSDate { - convenience init?(dateString:String) { - let formatter = NSDateFormatter() +extension Date { + init?(dateString: String) { + let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z" - formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") - if let d = formatter.dateFromString(dateString) { - self.init(timeInterval:0, sinceDate:d) + if let d = formatter.date(from: dateString) { + self.init(timeInterval:0, since:d) } else { - self.init(timeInterval:0, sinceDate:NSDate()) + self.init(timeInterval:0, since:Date()) return nil } } @@ -43,6 +42,6 @@ extension NSDate { } func toISOString() -> String { - return self.isoFormatter.stringFromDate(self) + return self.isoFormatter.string(from: self) } } diff --git a/SYNQueue/SYNQueue/SYNQueue.h b/SYNQueue/SYNQueue/SYNQueue.h old mode 100644 new mode 100755 diff --git a/SYNQueue/SYNQueue/SYNQueue.swift b/SYNQueue/SYNQueue/SYNQueue.swift old mode 100644 new mode 100755 index 4d8ce36..e1c10e6 --- a/SYNQueue/SYNQueue/SYNQueue.swift +++ b/SYNQueue/SYNQueue/SYNQueue.swift @@ -6,14 +6,14 @@ import Foundation /** -Log level for use in the SYNQueueLogProvider `log()` call - -- Trace: "Trace" -- Debug: "Debug" -- Info: "Info" -- Warning: "Warning" -- Error: "Error" -*/ + Log level for use in the SYNQueueLogProvider `log()` call + + - Trace: "Trace" + - Debug: "Debug" + - Info: "Info" + - Warning: "Warning" + - Error: "Error" + */ @objc public enum LogLevel: Int { case Trace = 0 @@ -24,49 +24,49 @@ public enum LogLevel: Int { public func toString() -> String { switch (self) { - case .Trace: return "Trace" - case .Debug: return "Debug" - case .Info: return "Info" - case .Warning: return "Warning" - case .Error: return "Error" + case .Trace: return "Trace" + case .Debug: return "Debug" + case .Info: return "Info" + case .Warning: return "Warning" + case .Error: return "Error" } } } /** -* Conform to this protocol to provide logging to SYNQueue -*/ + * Conform to this protocol to provide logging to SYNQueue + */ @objc public protocol SYNQueueLogProvider { - func log(level: LogLevel, _ msg: String) + func log(_ level: LogLevel, _ msg: String) } /** -* Conform to this protocol to provide serialization (persistence) to SYNQueue -*/ + * Conform to this protocol to provide serialization (persistence) to SYNQueue + */ @objc public protocol SYNQueueSerializationProvider { - func serializeTask(task: SYNQueueTask, queueName: String) - func deserializeTasksInQueue(queue: SYNQueue) -> [SYNQueueTask] - func removeTask(taskID: String, queue: SYNQueue) + func serializeTask(_ task: SYNQueueTask, queueName: String) + func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] + func removeTask(_ taskID: String, queue: SYNQueue) } /** -* SYNQueue is a generic queue with customizable serialization, logging, task handling, retries, and concurrency behavior -*/ + * SYNQueue is a generic queue with customizable serialization, logging, task handling, retries, and concurrency behavior + */ @objc -public class SYNQueue : NSOperationQueue { +open class SYNQueue : OperationQueue { /// The maximum number of times a task will be retried if it fails - public let maxRetries: Int + open let maxRetries: Int let serializationProvider: SYNQueueSerializationProvider? let logProvider: SYNQueueLogProvider? - var tasksMap = [String: SYNQueueTask]() + var taskMap = [String: SYNQueueTask]() var taskHandlers = [String: SYNTaskCallback]() let completionBlock: SYNTaskCompleteCallback? - public var tasks: [SYNQueueTask] { + open var tasks: [SYNQueueTask] { let array = operations var output = [SYNQueueTask]() @@ -80,22 +80,24 @@ public class SYNQueue : NSOperationQueue { } /** - Initializes a SYNQueue with the provided options - - - parameter queueName: The name of the queue - - parameter maxConcurrency: The maximum number of tasks to run in parallel - - parameter maxRetries: The maximum times a task will be retried if it fails - - parameter logProvider: An optional logger, nothing will be logged if this is nil - - parameter serializationProvider: An optional serializer, there will be no serialzation (persistence) if nil - - parameter completionBlock: The closure to call when a task finishes - - - returns: A new SYNQueue - */ - public required init(queueName: String, maxConcurrency: Int = 1, maxRetries: Int = 5, - logProvider: SYNQueueLogProvider? = nil, - serializationProvider: SYNQueueSerializationProvider? = nil, - completionBlock: SYNTaskCompleteCallback? = nil) - { + Initializes a SYNQueue with the provided options + + - parameter queueName: The name of the queue + - parameter maxConcurrency: The maximum number of tasks to run in parallel + - parameter maxRetries: The maximum times a task will be retried if it fails + - parameter logProvider: An optional logger, nothing will be logged if this is nil + - parameter serializationProvider: An optional serializer, there will be no serialzation (persistence) if nil + - parameter completionBlock: The closure to call when a task finishes + + - returns: A new SYNQueue + */ + public required init(queueName: String, + maxConcurrency: Int = 1, + maxRetries: Int = 5, + logProvider: SYNQueueLogProvider? = nil, + serializationProvider: SYNQueueSerializationProvider? = nil, + completionBlock: SYNTaskCompleteCallback? = nil) { + self.maxRetries = maxRetries self.logProvider = logProvider self.serializationProvider = serializationProvider @@ -108,45 +110,45 @@ public class SYNQueue : NSOperationQueue { } /** - Add a handler for a task type - - - parameter taskType: The task type for the handler - - parameter taskHandler: The handler for this particular task type, must be generic for the task type - */ - public func addTaskHandler(taskType: String, taskHandler:SYNTaskCallback) { + Add a handler for a task type + + - parameter taskType: The task type for the handler + - parameter taskHandler: The handler for this particular task type, must be generic for the task type + */ + open func addTaskHandler(_ taskType: String, taskHandler: @escaping SYNTaskCallback) { taskHandlers[taskType] = taskHandler } /** - Deserializes tasks that were serialized (persisted) - */ - public func loadSerializedTasks() { + Deserializes tasks that were serialized (persisted) + */ + open func loadSerializedTasks() { + self.pause() if let sp = serializationProvider { - let tasks = sp.deserializeTasksInQueue(self) - + let tasks = sp.deserializeTasks(self) for task in tasks { - task.setupDependencies(tasks) addDeserializedTask(task) } } + self.start() } - public func getTask(taskID: String) -> SYNQueueTask? { - return tasksMap[taskID] + open func getTask(_ taskID: String) -> SYNQueueTask? { + return taskMap[taskID] } /** - Adds a SYNQueueTask to the queue and serializes it - - - parameter op: A SYNQueueTask to execute on the queue - */ - override public func addOperation(op: NSOperation) { + Adds a SYNQueueTask to the queue and serializes it + + - parameter op: A SYNQueueTask to execute on the queue + */ + override open func addOperation(_ op: Operation) { if let task = op as? SYNQueueTask { - if tasksMap[task.taskID] != nil { + if taskMap[task.taskID] != nil { log(.Warning, "Attempted to add duplicate task \(task.taskID)") return } - tasksMap[task.taskID] = task + taskMap[task.taskID] = task // Serialize this operation if let sp = serializationProvider, let queueName = task.queue.name { @@ -158,8 +160,16 @@ public class SYNQueue : NSOperationQueue { super.addOperation(op) } - func addDeserializedTask(task: SYNQueueTask) { - if tasksMap[task.taskID] != nil { + open func start() { + self.isSuspended = false + } + + open func pause() { + self.isSuspended = true + } + + func addDeserializedTask(_ task: SYNQueueTask) { + if taskMap[task.taskID] != nil { log(.Warning, "Attempted to add duplicate deserialized task \(task.taskID)") return } @@ -168,7 +178,7 @@ public class SYNQueue : NSOperationQueue { super.addOperation(task) } - func runTask(task: SYNQueueTask) { + func runTask(_ task: SYNQueueTask) { if let handler = taskHandlers[task.taskType] { handler(task) } else { @@ -177,10 +187,10 @@ public class SYNQueue : NSOperationQueue { } } - func taskComplete(op: NSOperation) { + func taskComplete(_ op: Operation) { if let task = op as? SYNQueueTask { - tasksMap.removeValueForKey(task.taskID) - + taskMap.removeValue(forKey: task.taskID) + if let handler = completionBlock { handler(task.lastError, task) } @@ -192,7 +202,7 @@ public class SYNQueue : NSOperationQueue { } } - func log(level: LogLevel, _ msg: String) { + func log(_ level: LogLevel, _ msg: String) { logProvider?.log(level, msg) } } diff --git a/SYNQueue/SYNQueue/SYNQueueTask.swift b/SYNQueue/SYNQueue/SYNQueueTask.swift old mode 100644 new mode 100755 index 4f39c12..03039b6 --- a/SYNQueue/SYNQueue/SYNQueueTask.swift +++ b/SYNQueue/SYNQueue/SYNQueueTask.swift @@ -7,154 +7,142 @@ import Foundation public typealias SYNTaskCallback = (SYNQueueTask) -> Void public typealias SYNTaskCompleteCallback = (NSError?, SYNQueueTask) -> Void -public typealias JSONDictionary = [String: AnyObject?] +public typealias JSONDictionary = [String: Any?] + +private let MAX_RETRY_LIMIT = 3 + +private let keyTaskId = "taskId" +private let keyTaskType = "taskType" +private let keyTaskQos = "taskQos" +private let keyTaskData = "taskData" +private let keyTaskCreatedDate = "taskCreatedDate" +private let keyTaskRetries = "taskRetries" /** -* Represents a task to be executed on a SYNQueue -*/ + * Represents a task to be executed on a SYNQueue + */ @objc -public class SYNQueueTask : NSOperation { +open class SYNQueueTask : Operation { static let MIN_RETRY_DELAY = 0.2 static let MAX_RETRY_DELAY = 60.0 - public let queue: SYNQueue - public let taskID: String - public let taskType: String - public let data: AnyObject? - public let created: NSDate - public var started: NSDate? - public var retries: Int + open let queue: SYNQueue + open let taskID: String + open let taskType: String + open let data: Any? + open let created: Date + open var retries: Int - let dependencyStrs: [String] var lastError: NSError? var _executing: Bool = false var _finished: Bool = false - public override var name: String? { get { return taskID } set { } } - public override var asynchronous: Bool { return true } + open override var name: String? { get { return taskID } set { } } + open override var isAsynchronous: Bool { return true } - public override var executing: Bool { + open override var isExecuting: Bool { get { return _executing } set { - willChangeValueForKey("isExecuting") + willChangeValue(forKey: "isExecuting") _executing = newValue - didChangeValueForKey("isExecuting") + didChangeValue(forKey: "isExecuting") } } - public override var finished: Bool { + open override var isFinished: Bool { get { return _finished } set { - willChangeValueForKey("isFinished") + willChangeValue(forKey: "isFinished") _finished = newValue - didChangeValueForKey("isFinished") + didChangeValue(forKey: "isFinished") } } /** - Initializes a new SYNQueueTask with the following options - - - parameter queue: The queue that will execute the task - - parameter taskID: A unique identifier for the task, must be unique across app terminations, - otherwise dependencies will not work correctly - - parameter taskType: A type that will be used to group tasks together, tasks have to be generic with respect to their type - - parameter dependencyStrs: Identifiers for tasks that are dependencies of this task - - parameter data: The data that the task needs to operate on - - parameter created: When the task was created - - parameter started: When the task started executing - - parameter retries: Number of times this task has been retried after failing - - parameter queuePriority: The priority - - parameter qualityOfService: The quality of service + Initializes a new SYNQueueTask with the following options + + - parameter queue: The queue that will execute the task + - parameter taskID: A unique identifier for the task, must be unique across app terminations, + otherwise dependencies will not work correctly + - parameter taskType: A type that will be used to group tasks together, tasks have to be generic with respect to their type + - parameter data: The data that the task needs to operate on + - parameter created: When the task was created + - parameter retries: Number of times this task has been retried after failing + - parameter qualityOfService: The quality of service + + - returns: A new SYNQueueTask + */ + public init(queue: SYNQueue, + taskType: String, + data: Any? = nil, + retries: Int = MAX_RETRY_LIMIT, + qualityOfService: QualityOfService = .utility) { + + self.queue = queue + self.taskType = taskType + self.data = data + self.retries = retries + + self.taskID = UUID().uuidString + self.created = Date() + + super.init() + self.qualityOfService = qualityOfService + } - - returns: A new SYNQueueTask - */ - private init(queue: SYNQueue, taskID: String? = nil, taskType: String, - dependencyStrs: [String] = [], data: AnyObject? = nil, - created: NSDate = NSDate(), started: NSDate? = nil, retries: Int = 0, - queuePriority: NSOperationQueuePriority = .Normal, - qualityOfService: NSQualityOfService = .Utility) - { + // private initializer with mandatory params; to be used by the init + private init(queue: SYNQueue, + taskID: String, + taskType: String, + data: Any?, + created: Date, + retries: Int, + qualityOfService: QualityOfService) { + self.queue = queue - self.taskID = taskID ?? NSUUID().UUIDString + self.taskID = taskID self.taskType = taskType - self.dependencyStrs = dependencyStrs self.data = data self.created = created - self.started = started self.retries = retries super.init() - - self.queuePriority = queuePriority self.qualityOfService = qualityOfService } - - /** - Initializes a new SYNQueueTask with the following options - - - parameter queue: The queue that will execute the task - - parameter taskType: A type that will be used to group tasks together, tasks have to be generic with respect to their type - - parameter data: The data that the task needs to operate on - - parameter retries: Number of times this task has been retried after failing - - parameter queuePriority: The priority - - parameter qualityOfService: The quality of service - - - returns: A new SYNQueueTask - */ - public convenience init(queue: SYNQueue, type: String, data: AnyObject? = nil, retries: Int = 0, priority: NSOperationQueuePriority = .Normal, quality: NSQualityOfService = .Utility) { - self.init(queue: queue, taskType: type, data: data, retries: retries, queuePriority: priority, qualityOfService: quality) - } - - // For objective-c compatibility of convenience initializer - // See: http://sidhantgandhi.com/swift-default-parameter-values-in-convenience-initializers/ - public convenience init(queue: SYNQueue, taskType: String) { - self.init(queue: queue, type: taskType) - } - - /** - Initializes a SYNQueueTask from a dictionary - - - parameter dictionary: A dictionary that contains the data to reconstruct a task - - parameter queue: The queue that the task will execute on - - returns: A new SYNQueueTask - */ - public convenience init?(dictionary: JSONDictionary, queue: SYNQueue) { - if let taskID = dictionary["taskID"] as? String, - let taskType = dictionary["taskType"] as? String, - let dependencyStrs = dictionary["dependencies"] as? [String]? ?? [], - let queuePriority = dictionary["queuePriority"] as? Int, - let qualityOfService = dictionary["qualityOfService"] as? Int, - let data: AnyObject? = dictionary["data"] as AnyObject??, - let createdStr = dictionary["created"] as? String, - let startedStr: String? = dictionary["started"] as? String ?? nil, - let retries = dictionary["retries"] as? Int? ?? 0 - { - let created = NSDate(dateString: createdStr) ?? NSDate() - let started = (startedStr != nil) ? NSDate(dateString: startedStr!) : nil - let priority = NSOperationQueuePriority(rawValue: queuePriority) ?? .Normal - let qos = NSQualityOfService(rawValue: qualityOfService) ?? .Utility + public init?(dictionary: JSONDictionary, queue: SYNQueue) { + if let taskID = dictionary[keyTaskId] as? String, + let taskType = dictionary[keyTaskType] as? String, + let qualityOfService = dictionary[keyTaskQos] as? Int, + let data: Any? = dictionary[keyTaskData] as Any??, + let createdStr = dictionary[keyTaskCreatedDate] as? String, + let retries = dictionary[keyTaskRetries] as? Int? ?? 0 { + + self.queue = queue + self.taskID = taskID + self.taskType = taskType + self.data = data + self.created = Date(dateString: createdStr) ?? Date() + self.retries = retries + + super.init() + self.qualityOfService = QualityOfService(rawValue: qualityOfService) ?? .utility - self.init(queue: queue, taskID: taskID, taskType: taskType, - dependencyStrs: dependencyStrs, data: data, created: created, - started: started, retries: retries, queuePriority: priority, - qualityOfService: qos) } else { - self.init(queue: queue, taskID: "", taskType: "") return nil } } /** - Initializes a SYNQueueTask from JSON - - - parameter json: JSON from which the reconstruct the task - - parameter queue: The queue that the task will execute on - - - returns: A new SYNQueueTask - */ + Initializes a SYNQueueTask from JSON + + - parameter json: JSON from which the reconstruct the task + - parameter queue: The queue that the task will execute on + + - returns: A new SYNQueueTask + */ public convenience init?(json: String, queue: SYNQueue) { do { - if let dict = try fromJSON(json) as? [String: AnyObject] { + if let dict = try fromJSON(json) as? [String: Any] { self.init(dictionary: dict, queue: queue) } else { return nil @@ -165,50 +153,28 @@ public class SYNQueueTask : NSOperation { } /** - Setup the dependencies for the task - - - parameter allTasks: Array of SYNQueueTasks that are dependencies of this task - */ - public func setupDependencies(allTasks: [SYNQueueTask]) { - dependencyStrs.forEach { - (taskID: String) -> Void in - - let found = allTasks.filter({ taskID == $0.name }) - if let task = found.first { - self.addDependency(task) - } else { - let name = self.name ?? "(unknown)" - self.queue.log(.Warning, "Discarding missing dependency \(taskID) from \(name)") - } - } - } - - /** - Deconstruct the task to a dictionary, used to serialize the task - - - returns: A Dictionary representation of the task - */ - public func toDictionary() -> [String: AnyObject?] { - var dict = [String: AnyObject?]() - dict["taskID"] = self.taskID - dict["taskType"] = self.taskType - dict["dependencies"] = self.dependencyStrs - dict["queuePriority"] = self.queuePriority.rawValue - dict["qualityOfService"] = self.qualityOfService.rawValue - dict["data"] = self.data - dict["created"] = self.created.toISOString() - dict["started"] = (self.started != nil) ? self.started!.toISOString() : nil - dict["retries"] = self.retries + Deconstruct the task to a dictionary, used to serialize the task + + - returns: A Dictionary representation of the task + */ + open func toDictionary() -> [String: Any?] { + var dict = [String: Any?]() + dict[keyTaskId] = self.taskID as Any + dict[keyTaskType] = self.taskType as Any + dict[keyTaskQos] = self.qualityOfService.rawValue as Any + dict[keyTaskData] = self.data + dict[keyTaskCreatedDate] = self.created.toISOString() + dict[keyTaskRetries] = self.retries as Any return dict } /** - Deconstruct the task to a JSON string, used to serialize the task - - - returns: A JSON string representation of the task - */ - public func toJSONString() -> String? { + Deconstruct the task to a JSON string, used to serialize the task + + - returns: A JSON string representation of the task + */ + open func toJSONString() -> String? { // Serialize this task to a dictionary let dict = toDictionary() @@ -228,44 +194,44 @@ public class SYNQueueTask : NSOperation { } /** - Starts executing the task - */ - public override func start() { + Starts executing the task + */ + open override func start() { super.start() - executing = true + isExecuting = true run() } /** - Cancels the task - */ - public override func cancel() { + Cancels the task + */ + open override func cancel() { lastError = NSError(domain: "SYNQueue", code: -1, userInfo: [NSLocalizedDescriptionKey: "Task \(taskID) was cancelled"]) super.cancel() queue.log(.Debug, "Canceled task \(taskID)") - finished = true + isFinished = true } func run() { - if cancelled && !finished { finished = true } - if finished { return } + if isCancelled && !isFinished { isFinished = true } + if isFinished { return } queue.runTask(self) } /** - Call this to mark the task as completed, even if it failed. If it failed, we will use exponential backoff to keep retrying - the task until max number of retries is reached. Once this happens, we cancel the task. - - - parameter error: If the task failed, pass an error to indicate why - */ - public func completed(error: NSError?) { + Call this to mark the task as completed, even if it failed. If it failed, we will use exponential backoff to keep retrying + the task until max number of retries is reached. Once this happens, we cancel the task. + + - parameter error: If the task failed, pass an error to indicate why + */ + open func completed(_ error: NSError?) { // Check to make sure we're even executing, if not // just ignore the completed call - if (!executing) { + if (!isExecuting) { queue.log(.Debug, "Completion called on already completed task \(taskID)") return } @@ -275,22 +241,23 @@ public class SYNQueueTask : NSOperation { queue.log(.Warning, "Task \(taskID) failed with error: \(error)") // Check if we've exceeded the max allowed retries - if ++retries >= queue.maxRetries { + retries += 1 + if retries >= queue.maxRetries { queue.log(.Error, "Max retries exceeded for task \(taskID)") cancel() return } // Wait a bit (exponential backoff) and retry this task - let exp = Double(min(queue.maxRetries ?? 0, retries)) - let seconds:NSTimeInterval = min(SYNQueueTask.MAX_RETRY_DELAY, SYNQueueTask.MIN_RETRY_DELAY * pow(2.0, exp - 1)) + let exp = Double(min(queue.maxRetries , retries)) + let seconds:TimeInterval = min(SYNQueueTask.MAX_RETRY_DELAY, SYNQueueTask.MIN_RETRY_DELAY * pow(2.0, exp - 1)) queue.log(.Debug, "Waiting \(seconds) seconds to retry task \(taskID)") runInBackgroundAfter(seconds) { self.run() } } else { lastError = nil queue.log(.Debug, "Task \(taskID) completed") - finished = true + isFinished = true } } } diff --git a/SYNQueue/SYNQueue/Utils.swift b/SYNQueue/SYNQueue/Utils.swift old mode 100644 new mode 100755 index 2db3528..935ed6c --- a/SYNQueue/SYNQueue/Utils.swift +++ b/SYNQueue/SYNQueue/Utils.swift @@ -5,31 +5,24 @@ import Foundation -func runInBackgroundAfter(seconds: NSTimeInterval, callback:dispatch_block_t) { - let delta = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds) * Int64(NSEC_PER_SEC)) - dispatch_after(delta, dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), callback) +func runInBackgroundAfter(_ seconds: TimeInterval, callback:@escaping ()->()) { + let delta = DispatchTime.now() + Double(Int64(seconds) * Int64(NSEC_PER_SEC)) / Double(NSEC_PER_SEC) + DispatchQueue.global(qos: DispatchQoS.QoSClass.background).asyncAfter(deadline: delta, execute: callback) } -func synced(lock: AnyObject, closure: () -> ()) { - objc_sync_enter(lock) - closure() - objc_sync_exit(lock) +func runOnMainThread(_ callback:@escaping ()->()) { + DispatchQueue.main.async(execute: callback) } -func runOnMainThread(callback:dispatch_block_t) { - dispatch_async(dispatch_get_main_queue(), callback) +func toJSON(_ obj: Any) throws -> String? { + let json = try JSONSerialization.data(withJSONObject: obj, options: []) + return NSString(data: json, encoding: String.Encoding.utf8.rawValue) as String? } -func toJSON(obj: AnyObject) throws -> String? { - let json = try NSJSONSerialization.dataWithJSONObject(obj, options: []) - return NSString(data: json, encoding: NSUTF8StringEncoding) as String? -} - -func fromJSON(str: String) throws -> AnyObject? { - if let json = str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) { - let obj: AnyObject = try NSJSONSerialization.JSONObjectWithData(json, options: .AllowFragments) - return obj +func fromJSON(_ str: String) throws -> Any? { + if let json = str.data(using: String.Encoding.utf8, allowLossyConversion: false) { + let obj: Any = try JSONSerialization.jsonObject(with: json, options: .allowFragments) as Any + return obj } - return nil } diff --git a/SYNQueue/SYNQueueTests/ConsoleLogger.swift b/SYNQueue/SYNQueueTests/ConsoleLogger.swift old mode 100644 new mode 100755 index 2dc2a9b..58c13c7 --- a/SYNQueue/SYNQueueTests/ConsoleLogger.swift +++ b/SYNQueue/SYNQueueTests/ConsoleLogger.swift @@ -6,19 +6,16 @@ import Foundation import SYNQueue -func log(level: LogLevel, _ msg: String) { - return ConsoleLogger.log(level, msg) -} +import Foundation +func log(_ level: LogLevel, _ msg: String) { + return ConsoleLogger().log(level, msg) +} -class ConsoleLogger : SYNQueueLogProvider { - // MARK: - SYNQueueLogProvider Delegates +public class ConsoleLogger: SYNQueueLogProvider { - @objc func log(level: LogLevel, _ msg: String) { - return ConsoleLogger.log(level, msg) - } - - class func log(level: LogLevel, _ msg: String) { - runOnMainThread { print("[\(level.toString().uppercaseString)] \(msg)") } + public func log(_ level: LogLevel, _ msg: String) { + print("[\(level.toString())] \(msg)") } } + diff --git a/SYNQueue/SYNQueueTests/Info.plist b/SYNQueue/SYNQueueTests/Info.plist old mode 100644 new mode 100755 index c5f40d6..ba72822 --- a/SYNQueue/SYNQueueTests/Info.plist +++ b/SYNQueue/SYNQueueTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SYNQueue/SYNQueueTests/SYNQueueTests.swift b/SYNQueue/SYNQueueTests/SYNQueueTests.swift old mode 100644 new mode 100755 index 5baa310..ec93dfb --- a/SYNQueue/SYNQueueTests/SYNQueueTests.swift +++ b/SYNQueue/SYNQueueTests/SYNQueueTests.swift @@ -10,7 +10,7 @@ import XCTest class SYNQueueTests: XCTestCase { var logger = ConsoleLogger() - var serializer = NSUserDefaultsSerializer() + var serializer = UserDefaultsSerializer() let testTaskType = "testTaskType" override func setUp() { @@ -18,7 +18,7 @@ class SYNQueueTests: XCTestCase { // Put setup code here. This method is called before the invocation of each test method in the class. logger = ConsoleLogger() - serializer = NSUserDefaultsSerializer() + serializer = UserDefaultsSerializer() } @@ -43,7 +43,7 @@ class SYNQueueTests: XCTestCase { func testTaskCompletion() { - let taskCompletionExpectation = expectationWithDescription("taskCompletion") + let taskCompletionExpectation = expectation(description: "taskCompletion") let queue = SYNQueue(queueName: randomQueueName(), maxConcurrency: 3, maxRetries: 2, logProvider: logger, serializationProvider: serializer) { (error: NSError?, task: SYNQueueTask) -> Void in taskCompletionExpectation.fulfill() @@ -55,7 +55,7 @@ class SYNQueueTests: XCTestCase { XCTAssert(queue.operationCount == 1) - waitForExpectationsWithTimeout(5, handler: { error in + waitForExpectations(timeout: 5, handler: { error in XCTAssertNil(error, "Error") }) } @@ -70,7 +70,7 @@ class SYNQueueTests: XCTestCase { // Add a task to the queue queue!.addTaskHandler(testTaskType) { - NSThread.sleepForTimeInterval(2) + Thread.sleep(forTimeInterval: 2) $0.completed(nil) } let task = SYNQueueTask(queue: queue!, taskType: testTaskType) @@ -86,7 +86,7 @@ class SYNQueueTests: XCTestCase { queue2.loadSerializedTasks() XCTAssert(queue2.operationCount == 1) - let serializedTasks = queue2.serializationProvider?.deserializeTasksInQueue(queue2) + let serializedTasks = queue2.serializationProvider?.deserializeTasks(queue2) serializedTasks?.forEach({ (task: SYNQueueTask) -> () in queue2.serializationProvider?.removeTask(task.taskID, queue: queue2) }) @@ -98,12 +98,12 @@ class SYNQueueTests: XCTestCase { } queue.addTaskHandler(testTaskType) { - NSThread.sleepForTimeInterval(1) + Thread.sleep(forTimeInterval: 1) $0.completed(nil) } let task = SYNQueueTask(queue: queue, taskType: testTaskType) - self.measureBlock() { + self.measure() { queue.addOperation(task) } } @@ -111,5 +111,7 @@ class SYNQueueTests: XCTestCase { // MARK: Helper methods func randomQueueName() -> String { - return NSUUID().UUIDString + return UUID().uuidString } + + diff --git a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift b/SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift old mode 100644 new mode 100755 similarity index 61% rename from SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift rename to SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift index 24a2a46..b02b626 --- a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift +++ b/SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift @@ -6,16 +6,15 @@ import Foundation import SYNQueue - -class NSUserDefaultsSerializer : SYNQueueSerializationProvider { +class UserDefaultsSerializer: SYNQueueSerializationProvider { // MARK: - SYNQueueSerializationProvider Methods - @objc func serializeTask(task: SYNQueueTask, queueName: String) { + func serializeTask(_ task: SYNQueueTask, queueName: String) { if let serialized = task.toJSONString() { - let defaults = NSUserDefaults.standardUserDefaults() + let defaults = UserDefaults.standard var stringArray: [String] - if let curStringArray = defaults.stringArrayForKey(queueName) { + if let curStringArray = defaults.stringArray(forKey: queueName) { stringArray = curStringArray stringArray.append(serialized) } else { @@ -28,23 +27,22 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { } } - @objc func deserializeTasksInQueue(queue: SYNQueue) -> [SYNQueueTask] { - let defaults = NSUserDefaults.standardUserDefaults() + func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { + let defaults = UserDefaults.standard if let queueName = queue.name, - let stringArray = defaults.stringArrayForKey(queueName) - { + let stringArray = defaults.stringArray(forKey: queueName) { + return stringArray .map { return SYNQueueTask(json: $0, queue: queue) } .filter { return $0 != nil } .map { return $0! } } - return [] } - @objc func removeTask(taskID: String, queue: SYNQueue) { + func removeTask(_ taskID: String, queue: SYNQueue) { if let queueName = queue.name { - var curArray: [SYNQueueTask] = deserializeTasksInQueue(queue) + var curArray: [SYNQueueTask] = deserializeTasks(queue) curArray = curArray.filter { return $0.taskID != taskID } let stringArray = curArray @@ -52,8 +50,7 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { .filter { return $0 != nil } .map { return $0! } - let defaults = NSUserDefaults.standardUserDefaults() - defaults.setValue(stringArray, forKey: queueName) + UserDefaults.standard.setValue(stringArray, forKey: queueName) } } } diff --git a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index c47672c..6a2ac1f --- a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj +++ b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj @@ -7,19 +7,18 @@ objects = { /* Begin PBXBuildFile section */ + 0EB0B1B31EE7E85E00B43FB0 /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB0B1B21EE7E85E00B43FB0 /* Reachability.swift */; }; 9F70B5031B368AB500BDB945 /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F70B5021B368AB500BDB945 /* ConsoleLogger.swift */; }; 9FD63F971B333BDC001BD09A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63F961B333BDC001BD09A /* AppDelegate.swift */; }; 9FD63F991B333BDC001BD09A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63F981B333BDC001BD09A /* ViewController.swift */; }; 9FD63F9E1B333BDC001BD09A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FD63F9D1B333BDC001BD09A /* Images.xcassets */; }; 9FD63FA11B333BDC001BD09A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9FD63F9F1B333BDC001BD09A /* LaunchScreen.xib */; }; 9FD63FAD1B333BDC001BD09A /* SYNQueueDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FAC1B333BDC001BD09A /* SYNQueueDemoTests.swift */; }; - 9FD63FB91B333E15001BD09A /* SYNQueue.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FD63FB81B333E15001BD09A /* SYNQueue.framework */; }; - 9FD63FBA1B333E15001BD09A /* SYNQueue.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9FD63FB81B333E15001BD09A /* SYNQueue.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9FD63FC51B338BA5001BD09A /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FC41B338BA5001BD09A /* TaskCell.swift */; }; - 9FD63FC71B33EA2F001BD09A /* NSUserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FC61B33EA2F001BD09A /* NSUserDefaultsSerializer.swift */; }; + 9FD63FC71B33EA2F001BD09A /* UserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FC61B33EA2F001BD09A /* UserDefaultsSerializer.swift */; }; 9FD63FCD1B3417DF001BD09A /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FCC1B3417DF001BD09A /* Utils.swift */; }; - DC67E47E1BB0F01200F3D9E2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC67E47D1BB0F01200F3D9E2 /* Main.storyboard */; settings = {ASSET_TAGS = (); }; }; - DC67E4801BB0F1BD00F3D9E2 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC67E47F1BB0F1BD00F3D9E2 /* SettingsViewController.swift */; settings = {ASSET_TAGS = (); }; }; + DC67E47E1BB0F01200F3D9E2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DC67E47D1BB0F01200F3D9E2 /* Main.storyboard */; }; + DC67E4801BB0F1BD00F3D9E2 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC67E47F1BB0F1BD00F3D9E2 /* SettingsViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -39,7 +38,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 9FD63FBA1B333E15001BD09A /* SYNQueue.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -47,6 +45,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0EB0B1B21EE7E85E00B43FB0 /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; }; 9F70B5021B368AB500BDB945 /* ConsoleLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = ""; }; 9FD63F911B333BDC001BD09A /* SYNQueueDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SYNQueueDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9FD63F951B333BDC001BD09A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -57,9 +56,8 @@ 9FD63FA61B333BDC001BD09A /* SYNQueueDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SYNQueueDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9FD63FAB1B333BDC001BD09A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9FD63FAC1B333BDC001BD09A /* SYNQueueDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SYNQueueDemoTests.swift; sourceTree = ""; }; - 9FD63FB81B333E15001BD09A /* SYNQueue.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SYNQueue.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9FD63FC41B338BA5001BD09A /* TaskCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskCell.swift; sourceTree = ""; }; - 9FD63FC61B33EA2F001BD09A /* NSUserDefaultsSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSUserDefaultsSerializer.swift; sourceTree = ""; }; + 9FD63FC61B33EA2F001BD09A /* UserDefaultsSerializer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsSerializer.swift; sourceTree = ""; }; 9FD63FCC1B3417DF001BD09A /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; DC67E47D1BB0F01200F3D9E2 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; DC67E47F1BB0F1BD00F3D9E2 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; @@ -70,7 +68,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9FD63FB91B333E15001BD09A /* SYNQueue.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -87,7 +84,6 @@ 9FD63F881B333BDC001BD09A = { isa = PBXGroup; children = ( - 9FD63FB81B333E15001BD09A /* SYNQueue.framework */, 9FD63F931B333BDC001BD09A /* SYNQueueDemo */, 9FD63FA91B333BDC001BD09A /* SYNQueueDemoTests */, 9FD63F921B333BDC001BD09A /* Products */, @@ -109,13 +105,14 @@ 9FD63F9D1B333BDC001BD09A /* Images.xcassets */, 9FD63F941B333BDC001BD09A /* Supporting Files */, 9FD63F961B333BDC001BD09A /* AppDelegate.swift */, + 0EB0B1B21EE7E85E00B43FB0 /* Reachability.swift */, 9F70B5021B368AB500BDB945 /* ConsoleLogger.swift */, 9FD63F981B333BDC001BD09A /* ViewController.swift */, DC67E47F1BB0F1BD00F3D9E2 /* SettingsViewController.swift */, DC67E47D1BB0F01200F3D9E2 /* Main.storyboard */, 9FD63F9F1B333BDC001BD09A /* LaunchScreen.xib */, 9FD63FC41B338BA5001BD09A /* TaskCell.swift */, - 9FD63FC61B33EA2F001BD09A /* NSUserDefaultsSerializer.swift */, + 9FD63FC61B33EA2F001BD09A /* UserDefaultsSerializer.swift */, 9FD63FCC1B3417DF001BD09A /* Utils.swift */, ); path = SYNQueueDemo; @@ -193,15 +190,17 @@ attributes = { LastSwiftMigration = 0700; LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0630; + LastUpgradeCheck = 0830; ORGANIZATIONNAME = Syntertainment; TargetAttributes = { 9FD63F901B333BDC001BD09A = { CreatedOnToolsVersion = 6.3.2; DevelopmentTeam = SBKBKHJ464; + LastSwiftMigration = 0830; }; 9FD63FA51B333BDC001BD09A = { CreatedOnToolsVersion = 6.3.2; + LastSwiftMigration = 0830; TestTargetID = 9FD63F901B333BDC001BD09A; }; }; @@ -256,7 +255,8 @@ 9FD63FC51B338BA5001BD09A /* TaskCell.swift in Sources */, 9F70B5031B368AB500BDB945 /* ConsoleLogger.swift in Sources */, 9FD63F971B333BDC001BD09A /* AppDelegate.swift in Sources */, - 9FD63FC71B33EA2F001BD09A /* NSUserDefaultsSerializer.swift in Sources */, + 0EB0B1B31EE7E85E00B43FB0 /* Reachability.swift in Sources */, + 9FD63FC71B33EA2F001BD09A /* UserDefaultsSerializer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -303,14 +303,17 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -348,8 +351,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -368,6 +373,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -376,28 +382,32 @@ 9FD63FB11B333BDC001BD09A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = SYNQueueDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Debug; }; 9FD63FB21B333BDC001BD09A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = SYNQueueDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -411,7 +421,9 @@ ); INFOPLIST_FILE = SYNQueueDemoTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SYNQueueDemo.app/SYNQueueDemo"; }; name = Debug; @@ -422,7 +434,9 @@ BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = SYNQueueDemoTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SYNQueueDemo.app/SYNQueueDemo"; }; name = Release; diff --git a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SYNQueueDemo/SYNQueueDemo/AppDelegate.swift b/SYNQueueDemo/SYNQueueDemo/AppDelegate.swift old mode 100644 new mode 100755 index 16a63b2..c9293fe --- a/SYNQueueDemo/SYNQueueDemo/AppDelegate.swift +++ b/SYNQueueDemo/SYNQueueDemo/AppDelegate.swift @@ -11,30 +11,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } - func applicationWillResignActive(application: UIApplication) { + func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. } - func applicationDidEnterBackground(application: UIApplication) { + func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. } - func applicationWillEnterForeground(application: UIApplication) { + func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. } - func applicationDidBecomeActive(application: UIApplication) { + func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. } - func applicationWillTerminate(application: UIApplication) { + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } diff --git a/SYNQueueDemo/SYNQueueDemo/Base.lproj/LaunchScreen.xib b/SYNQueueDemo/SYNQueueDemo/Base.lproj/LaunchScreen.xib old mode 100644 new mode 100755 diff --git a/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift b/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift old mode 100644 new mode 100755 index 2dc2a9b..fdc9b96 --- a/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift +++ b/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift @@ -6,19 +6,15 @@ import Foundation import SYNQueue -func log(level: LogLevel, _ msg: String) { - return ConsoleLogger.log(level, msg) -} +import Foundation +func log(_ level: LogLevel, _ msg: String) { + return ConsoleLogger().log(level, msg) +} -class ConsoleLogger : SYNQueueLogProvider { - // MARK: - SYNQueueLogProvider Delegates - - @objc func log(level: LogLevel, _ msg: String) { - return ConsoleLogger.log(level, msg) - } +public class ConsoleLogger: SYNQueueLogProvider { - class func log(level: LogLevel, _ msg: String) { - runOnMainThread { print("[\(level.toString().uppercaseString)] \(msg)") } + public func log(_ level: LogLevel, _ msg: String) { + print("[\(level.toString())] \(msg)") } } diff --git a/SYNQueueDemo/SYNQueueDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/SYNQueueDemo/SYNQueueDemo/Images.xcassets/AppIcon.appiconset/Contents.json old mode 100644 new mode 100755 diff --git a/SYNQueueDemo/SYNQueueDemo/Info.plist b/SYNQueueDemo/SYNQueueDemo/Info.plist old mode 100644 new mode 100755 index 56c0cc3..0180b3e --- a/SYNQueueDemo/SYNQueueDemo/Info.plist +++ b/SYNQueueDemo/SYNQueueDemo/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SYNQueueDemo/SYNQueueDemo/Main.storyboard b/SYNQueueDemo/SYNQueueDemo/Main.storyboard old mode 100644 new mode 100755 diff --git a/SYNQueueDemo/SYNQueueDemo/Reachability.swift b/SYNQueueDemo/SYNQueueDemo/Reachability.swift new file mode 100644 index 0000000..721178a --- /dev/null +++ b/SYNQueueDemo/SYNQueueDemo/Reachability.swift @@ -0,0 +1,298 @@ +/* + Copyright (c) 2014, Ashley Mills + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +import SystemConfiguration +import Foundation + +public enum ReachabilityError: Error { + case FailedToCreateWithAddress(sockaddr_in) + case FailedToCreateWithHostname(String) + case UnableToSetCallback + case UnableToSetDispatchQueue +} + +public let ReachabilityChangedNotification = NSNotification.Name("ReachabilityChangedNotification") + +func callback(reachability:SCNetworkReachability, flags: SCNetworkReachabilityFlags, info: UnsafeMutableRawPointer?) { + + guard let info = info else { return } + + let reachability = Unmanaged.fromOpaque(info).takeUnretainedValue() + + DispatchQueue.main.async { + reachability.reachabilityChanged() + } +} + +public class Reachability { + + public typealias NetworkReachable = (Reachability) -> () + public typealias NetworkUnreachable = (Reachability) -> () + + public enum NetworkStatus: CustomStringConvertible { + + case notReachable, reachableViaWiFi, reachableViaWWAN + + public var description: String { + switch self { + case .reachableViaWWAN: return "Cellular" + case .reachableViaWiFi: return "WiFi" + case .notReachable: return "No Connection" + } + } + } + + public var whenReachable: NetworkReachable? + public var whenUnreachable: NetworkUnreachable? + public var reachableOnWWAN: Bool + + // The notification center on which "reachability changed" events are being posted + public var notificationCenter: NotificationCenter = NotificationCenter.default + + public var currentReachabilityString: String { + return "\(currentReachabilityStatus)" + } + + public var currentReachabilityStatus: NetworkStatus { + guard isReachable else { return .notReachable } + + if isReachableViaWiFi { + return .reachableViaWiFi + } + if isRunningOnDevice { + return .reachableViaWWAN + } + + return .notReachable + } + + fileprivate var previousFlags: SCNetworkReachabilityFlags? + + fileprivate var isRunningOnDevice: Bool = { + #if (arch(i386) || arch(x86_64)) && os(iOS) + return false + #else + return true + #endif + }() + + fileprivate var notifierRunning = false + fileprivate var reachabilityRef: SCNetworkReachability? + + fileprivate let reachabilitySerialQueue = DispatchQueue(label: "uk.co.ashleymills.reachability") + + required public init(reachabilityRef: SCNetworkReachability) { + reachableOnWWAN = true + self.reachabilityRef = reachabilityRef + } + + public convenience init?(hostname: String) { + + guard let ref = SCNetworkReachabilityCreateWithName(nil, hostname) else { return nil } + + self.init(reachabilityRef: ref) + } + + public convenience init?() { + + var zeroAddress = sockaddr() + zeroAddress.sa_len = UInt8(MemoryLayout.size) + zeroAddress.sa_family = sa_family_t(AF_INET) + + guard let ref: SCNetworkReachability = withUnsafePointer(to: &zeroAddress, { + SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) + }) else { return nil } + + self.init(reachabilityRef: ref) + } + + deinit { + stopNotifier() + + reachabilityRef = nil + whenReachable = nil + whenUnreachable = nil + } +} + +public extension Reachability { + + // MARK: - *** Notifier methods *** + func startNotifier() throws { + + guard let reachabilityRef = reachabilityRef, !notifierRunning else { return } + + var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) + context.info = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) + if !SCNetworkReachabilitySetCallback(reachabilityRef, callback, &context) { + stopNotifier() + throw ReachabilityError.UnableToSetCallback + } + + if !SCNetworkReachabilitySetDispatchQueue(reachabilityRef, reachabilitySerialQueue) { + stopNotifier() + throw ReachabilityError.UnableToSetDispatchQueue + } + + // Perform an initial check + reachabilitySerialQueue.async { + self.reachabilityChanged() + } + + notifierRunning = true + } + + func stopNotifier() { + defer { notifierRunning = false } + guard let reachabilityRef = reachabilityRef else { return } + + SCNetworkReachabilitySetCallback(reachabilityRef, nil, nil) + SCNetworkReachabilitySetDispatchQueue(reachabilityRef, nil) + } + + // MARK: - *** Connection test methods *** + var isReachable: Bool { + + guard isReachableFlagSet else { return false } + + if isConnectionRequiredAndTransientFlagSet { + return false + } + + if isRunningOnDevice { + if isOnWWANFlagSet && !reachableOnWWAN { + // We don't want to connect when on 3G. + return false + } + } + + return true + } + + var isReachableViaWWAN: Bool { + // Check we're not on the simulator, we're REACHABLE and check we're on WWAN + return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet + } + + var isReachableViaWiFi: Bool { + + // Check we're reachable + guard isReachableFlagSet else { return false } + + // If reachable we're reachable, but not on an iOS device (i.e. simulator), we must be on WiFi + guard isRunningOnDevice else { return true } + + // Check we're NOT on WWAN + return !isOnWWANFlagSet + } + + var description: String { + + let W = isRunningOnDevice ? (isOnWWANFlagSet ? "W" : "-") : "X" + let R = isReachableFlagSet ? "R" : "-" + let c = isConnectionRequiredFlagSet ? "c" : "-" + let t = isTransientConnectionFlagSet ? "t" : "-" + let i = isInterventionRequiredFlagSet ? "i" : "-" + let C = isConnectionOnTrafficFlagSet ? "C" : "-" + let D = isConnectionOnDemandFlagSet ? "D" : "-" + let l = isLocalAddressFlagSet ? "l" : "-" + let d = isDirectFlagSet ? "d" : "-" + + return "\(W)\(R) \(c)\(t)\(i)\(C)\(D)\(l)\(d)" + } +} + +fileprivate extension Reachability { + + func reachabilityChanged() { + + let flags = reachabilityFlags + + guard previousFlags != flags else { return } + + let block = isReachable ? whenReachable : whenUnreachable + block?(self) + + self.notificationCenter.post(name: ReachabilityChangedNotification, object:self) + + previousFlags = flags + } + + var isOnWWANFlagSet: Bool { + #if os(iOS) + return reachabilityFlags.contains(.isWWAN) + #else + return false + #endif + } + var isReachableFlagSet: Bool { + return reachabilityFlags.contains(.reachable) + } + var isConnectionRequiredFlagSet: Bool { + return reachabilityFlags.contains(.connectionRequired) + } + var isInterventionRequiredFlagSet: Bool { + return reachabilityFlags.contains(.interventionRequired) + } + var isConnectionOnTrafficFlagSet: Bool { + return reachabilityFlags.contains(.connectionOnTraffic) + } + var isConnectionOnDemandFlagSet: Bool { + return reachabilityFlags.contains(.connectionOnDemand) + } + var isConnectionOnTrafficOrDemandFlagSet: Bool { + return !reachabilityFlags.intersection([.connectionOnTraffic, .connectionOnDemand]).isEmpty + } + var isTransientConnectionFlagSet: Bool { + return reachabilityFlags.contains(.transientConnection) + } + var isLocalAddressFlagSet: Bool { + return reachabilityFlags.contains(.isLocalAddress) + } + var isDirectFlagSet: Bool { + return reachabilityFlags.contains(.isDirect) + } + var isConnectionRequiredAndTransientFlagSet: Bool { + return reachabilityFlags.intersection([.connectionRequired, .transientConnection]) == [.connectionRequired, .transientConnection] + } + + var reachabilityFlags: SCNetworkReachabilityFlags { + + guard let reachabilityRef = reachabilityRef else { return SCNetworkReachabilityFlags() } + + var flags = SCNetworkReachabilityFlags() + let gotFlags = withUnsafeMutablePointer(to: &flags) { + SCNetworkReachabilityGetFlags(reachabilityRef, UnsafeMutablePointer($0)) + } + + if gotFlags { + return flags + } else { + return SCNetworkReachabilityFlags() + } + } +} diff --git a/SYNQueueDemo/SYNQueueDemo/SettingsViewController.swift b/SYNQueueDemo/SYNQueueDemo/SettingsViewController.swift old mode 100644 new mode 100755 index 73b8363..a446458 --- a/SYNQueueDemo/SYNQueueDemo/SettingsViewController.swift +++ b/SYNQueueDemo/SYNQueueDemo/SettingsViewController.swift @@ -16,15 +16,15 @@ class SettingsViewController: UITableViewController { override func viewDidLoad() { super.viewDidLoad() - self.dependencySwitch.on = NSUserDefaults.standardUserDefaults().boolForKey(kAddDependencySettingKey) - self.autocompleteTaskSwitch.on = NSUserDefaults.standardUserDefaults().boolForKey(kAutocompleteTaskSettingKey) + self.dependencySwitch.isOn = UserDefaults.standard.bool(forKey: kAddDependencySettingKey) + self.autocompleteTaskSwitch.isOn = UserDefaults.standard.bool(forKey: kAutocompleteTaskSettingKey) } - @IBAction func addDependencySwitchToggled(sender: UISwitch) { - NSUserDefaults.standardUserDefaults().setBool(sender.on, forKey: kAddDependencySettingKey) + @IBAction func addDependencySwitchToggled(_ sender: UISwitch) { + UserDefaults.standard.set(sender.isOn, forKey: kAddDependencySettingKey) } - @IBAction func autocompleteTaskSwitchToggled(sender: UISwitch) { - NSUserDefaults.standardUserDefaults().setBool(sender.on, forKey: kAutocompleteTaskSettingKey) + @IBAction func autocompleteTaskSwitchToggled(_ sender: UISwitch) { + UserDefaults.standard.set(sender.isOn, forKey: kAutocompleteTaskSettingKey) } -} \ No newline at end of file +} diff --git a/SYNQueueDemo/SYNQueueDemo/TaskCell.swift b/SYNQueueDemo/SYNQueueDemo/TaskCell.swift old mode 100644 new mode 100755 index e45e33b..8dc1c63 --- a/SYNQueueDemo/SYNQueueDemo/TaskCell.swift +++ b/SYNQueueDemo/SYNQueueDemo/TaskCell.swift @@ -13,13 +13,13 @@ class TaskCell : UICollectionViewCell { weak var task: SYNQueueTask? = nil - @IBAction func succeedTapped(sender: UIButton) { + @IBAction func succeedTapped(_ sender: UIButton) { if let task = task { task.completed(nil) } } - @IBAction func failTapped(sender: UIButton) { + @IBAction func failTapped(_ sender: UIButton) { if let task = task { let err = error("User tapped Fail on task \(task.taskID)") task.completed(err) diff --git a/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift b/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift old mode 100644 new mode 100755 similarity index 57% rename from SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift rename to SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift index 24a2a46..dfa315d --- a/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift +++ b/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift @@ -6,45 +6,40 @@ import Foundation import SYNQueue - -class NSUserDefaultsSerializer : SYNQueueSerializationProvider { +class UserDefaultsSerializer : SYNQueueSerializationProvider { // MARK: - SYNQueueSerializationProvider Methods - @objc func serializeTask(task: SYNQueueTask, queueName: String) { + func serializeTask(_ task: SYNQueueTask, queueName: String) { if let serialized = task.toJSONString() { - let defaults = NSUserDefaults.standardUserDefaults() var stringArray: [String] - - if let curStringArray = defaults.stringArrayForKey(queueName) { + if let curStringArray = UserDefaults.standard.stringArray(forKey: queueName) { stringArray = curStringArray stringArray.append(serialized) } else { stringArray = [serialized] } - defaults.setValue(stringArray, forKey: queueName) + UserDefaults.standard.set(stringArray, forKey: queueName) + UserDefaults.standard.synchronize() } else { log(.Error, "Failed to serialize task \(task.taskID) in queue \(queueName)") } } - @objc func deserializeTasksInQueue(queue: SYNQueue) -> [SYNQueueTask] { - let defaults = NSUserDefaults.standardUserDefaults() + func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { if let queueName = queue.name, - let stringArray = defaults.stringArrayForKey(queueName) - { + let stringArray = UserDefaults.standard.stringArray(forKey: queueName) { return stringArray .map { return SYNQueueTask(json: $0, queue: queue) } .filter { return $0 != nil } .map { return $0! } } - return [] } - @objc func removeTask(taskID: String, queue: SYNQueue) { + func removeTask(_ taskID: String, queue: SYNQueue) { if let queueName = queue.name { - var curArray: [SYNQueueTask] = deserializeTasksInQueue(queue) + var curArray: [SYNQueueTask] = deserializeTasks(queue) curArray = curArray.filter { return $0.taskID != taskID } let stringArray = curArray @@ -52,8 +47,8 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { .filter { return $0 != nil } .map { return $0! } - let defaults = NSUserDefaults.standardUserDefaults() - defaults.setValue(stringArray, forKey: queueName) + UserDefaults.standard.set(stringArray, forKey: queueName) + UserDefaults.standard.synchronize() } } } diff --git a/SYNQueueDemo/SYNQueueDemo/Utils.swift b/SYNQueueDemo/SYNQueueDemo/Utils.swift old mode 100644 new mode 100755 index 5f88c05..52fbeed --- a/SYNQueueDemo/SYNQueueDemo/Utils.swift +++ b/SYNQueueDemo/SYNQueueDemo/Utils.swift @@ -5,13 +5,37 @@ import Foundation import SYNQueue +// FIXME: comparison operators with optionals were removed from the Swift Standard Libary. +// Consider refactoring the code to use the non-optional operators. +fileprivate func < (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l < r + case (nil, _?): + return true + default: + return false + } +} + +// FIXME: comparison operators with optionals were removed from the Swift Standard Libary. +// Consider refactoring the code to use the non-optional operators. +fileprivate func > (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l > r + default: + return rhs < lhs + } +} + -func arrayMax(array: [T]) -> T? { +func arrayMax(_ array: [T]) -> T? { return array.reduce(array.first) { return $0 > $1 ? $0 : $1 } } -func findIndex(array: [T], _ valueToFind: T) -> Int? { - for (index, value) in array.enumerate() { +func findIndex(_ array: [T], _ valueToFind: T) -> Int? { + for (index, value) in array.enumerated() { if value == valueToFind { return index } @@ -19,16 +43,16 @@ func findIndex(array: [T], _ valueToFind: T) -> Int? { return nil } -func runOnMainThread(callback:dispatch_block_t) { - dispatch_async(dispatch_get_main_queue(), callback) +func runOnMainThread(_ callback: @escaping ()->()) { + DispatchQueue.main.async(execute: callback) } -func runOnMainThreadAfterDelay(delay:Double, _ callback:()->()) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), { () -> Void in +func runOnMainThreadAfterDelay(_ delay:Double, _ callback: @escaping ()->()) { + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: { () -> Void in callback() }) } -func error(msg: String) -> NSError { +func error(_ msg: String) -> NSError { return NSError(domain: "Error", code: -1, userInfo: [NSLocalizedDescriptionKey: msg]) } diff --git a/SYNQueueDemo/SYNQueueDemo/ViewController.swift b/SYNQueueDemo/SYNQueueDemo/ViewController.swift old mode 100644 new mode 100755 index 5a4dd0f..cceff89 --- a/SYNQueueDemo/SYNQueueDemo/ViewController.swift +++ b/SYNQueueDemo/SYNQueueDemo/ViewController.swift @@ -10,12 +10,17 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection @IBOutlet weak var progressView: UIProgressView! @IBOutlet weak var collectionView: UICollectionView! + let reachability = Reachability.init() + var totalTasksSeen = 0 - var nextTaskID = 1 + lazy var queue: SYNQueue = { - return SYNQueue(queueName: "myQueue", maxConcurrency: 2, maxRetries: 3, - logProvider: ConsoleLogger(), serializationProvider: NSUserDefaultsSerializer(), - completionBlock: { [weak self] in self?.taskComplete($0, $1) }) + return SYNQueue(queueName: "myQueue", + maxConcurrency: 2, + maxRetries: 3, + logProvider: ConsoleLogger(), + serializationProvider: UserDefaultsSerializer(), + completionBlock: { [weak self] in self?.taskComplete($0, $1) }) }() // MARK: - UIViewController Overrides @@ -25,26 +30,21 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection queue.addTaskHandler("cellTask", taskHandler: taskHandler) queue.loadSerializedTasks() - - let taskIDs = queue.operations - .map { return $0 as! SYNQueueTask } - .map { return Int($0.taskID) ?? 0 } - nextTaskID = (arrayMax(taskIDs) ?? 0) + 1 } - + override func viewDidLayoutSubviews() { if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { - flowLayout.itemSize = CGSizeMake(collectionView.bounds.size.width, 50) + flowLayout.itemSize = CGSize(width: collectionView.bounds.size.width, height: 50) } } - override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) { + override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) { collectionView.performBatchUpdates(nil, completion: nil) } // MARK: - SYNQueueTask Handling - func taskHandler(task: SYNQueueTask) { + func taskHandler(_ task: SYNQueueTask) { // NOTE: Tasks are not actually handled here like usual since task // completion in this example is based on user interaction, unless // we enable the setting for task autocompletion @@ -55,7 +55,7 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection // let data = task.data // Here, for example, we just auto complete the task - let taskShouldAutocomplete = NSUserDefaults.standardUserDefaults().boolForKey(kAutocompleteTaskSettingKey) + let taskShouldAutocomplete = UserDefaults.standard.bool(forKey: kAutocompleteTaskSettingKey) if taskShouldAutocomplete { // Set task completion after 3 seconds runOnMainThreadAfterDelay(3, { () -> () in @@ -64,10 +64,9 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection } runOnMainThread { self.collectionView.reloadData() } - } - func taskComplete(error: NSError?, _ task: SYNQueueTask) { + func taskComplete(_ error: NSError?, _ task: SYNQueueTask) { if let error = error { log(.Error, "Task \(task.taskID) failed with error: \(error)") } else { @@ -75,7 +74,6 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection } if queue.operationCount == 0 { - nextTaskID = 1 totalTasksSeen = 0 } @@ -86,31 +84,29 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection // MARK: - UICollectionView Delegates - func collectionView(collectionView: UICollectionView, numberOfItemsInSection - section: Int) -> Int - { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection + section: Int) -> Int { return queue.operationCount } - func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath - indexPath: NSIndexPath) -> UICollectionViewCell - { - let cell = collectionView.dequeueReusableCellWithReuseIdentifier( - "TaskCell", forIndexPath: indexPath) as! TaskCell - cell.backgroundColor = UIColor.redColor() + func collectionView(_ collectionView: UICollectionView, cellForItemAt + indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: "TaskCell", for: indexPath) as! TaskCell + cell.backgroundColor = UIColor.red if let task = queue.operations[indexPath.item] as? SYNQueueTask { cell.task = task cell.nameLabel.text = "task \(task.taskID)" - let taskShouldAutocomplete = NSUserDefaults.standardUserDefaults().boolForKey(kAutocompleteTaskSettingKey) - if task.executing && !taskShouldAutocomplete { - cell.backgroundColor = UIColor.blueColor() - cell.failButton.enabled = true - cell.succeedButton.enabled = true + let taskShouldAutocomplete = UserDefaults.standard.bool(forKey: kAutocompleteTaskSettingKey) + if task.isExecuting && !taskShouldAutocomplete { + cell.backgroundColor = UIColor.blue + cell.failButton.isEnabled = true + cell.succeedButton.isEnabled = true } else { - cell.backgroundColor = UIColor.grayColor() - cell.succeedButton.enabled = false - cell.failButton.enabled = false + cell.backgroundColor = UIColor.gray + cell.succeedButton.isEnabled = false + cell.failButton.isEnabled = false } } @@ -119,17 +115,12 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection // MARK: - IBActions - @IBAction func addTapped(sender: UIButton) { - let taskID1 = nextTaskID++ - let task1 = SYNQueueTask(queue: queue, taskID: String(taskID1), - taskType: "cellTask", dependencyStrs: [], data: [:]) + @IBAction func addTapped(_ sender: UIButton) { - let shouldAddDependency = NSUserDefaults.standardUserDefaults().boolForKey(kAddDependencySettingKey) - if shouldAddDependency { - let taskID2 = nextTaskID++ - let task2 = SYNQueueTask(queue: queue, taskID: String(taskID2), - taskType: "cellTask", dependencyStrs: [], data: [:]) - + let task1 = SYNQueueTask(queue: queue, taskType: "cellTask") + + if UserDefaults.standard.bool(forKey: kAddDependencySettingKey) { + let task2 = SYNQueueTask(queue: queue, taskType: "cellTask") // Make the first task dependent on the second task1.addDependency(task2) queue.addOperation(task2) @@ -142,13 +133,11 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection collectionView.reloadData() } - @IBAction func removeTapped(sender: UIButton) { + @IBAction func removeTapped(_ sender: UIButton) { // Find the first task in the list if let task = queue.operations.first as? SYNQueueTask { log(.Info, "Removing task \(task.taskID)") - task.cancel() - collectionView.reloadData() } } @@ -158,7 +147,6 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection func updateProgress() { let tasks = queue.tasks let progress = Double(totalTasksSeen - tasks.count) / Double(totalTasksSeen) - runOnMainThread { self.progressView.progress = Float(progress) } } } diff --git a/SYNQueueDemo/SYNQueueDemoTests/Info.plist b/SYNQueueDemo/SYNQueueDemoTests/Info.plist old mode 100644 new mode 100755 index c5f40d6..ba72822 --- a/SYNQueueDemo/SYNQueueDemoTests/Info.plist +++ b/SYNQueueDemo/SYNQueueDemoTests/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.SYNQueue.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/SYNQueueDemo/SYNQueueDemoTests/SYNQueueDemoTests.swift b/SYNQueueDemo/SYNQueueDemoTests/SYNQueueDemoTests.swift old mode 100644 new mode 100755 index 00108ac..55c6ea1 --- a/SYNQueueDemo/SYNQueueDemoTests/SYNQueueDemoTests.swift +++ b/SYNQueueDemo/SYNQueueDemoTests/SYNQueueDemoTests.swift @@ -25,7 +25,7 @@ class SYNQueueDemoTests: XCTestCase { func testPerformanceExample() { // This is an example of a performance test case. - self.measureBlock() { + self.measure() { // Put the code you want to measure the time of here. } } diff --git a/image/logo.png b/image/logo.png old mode 100644 new mode 100755 diff --git a/image/logo.sketch b/image/logo.sketch old mode 100644 new mode 100755