From 05e407b5ebbc54ba4ec87b9aa928b8bcfcf282aa Mon Sep 17 00:00:00 2001 From: Rajavelu Chandrasekaran Date: Thu, 8 Jun 2017 13:29:48 +0800 Subject: [PATCH 1/6] Code has been fixed for swift 3. Fixed an issue that prevented the tasks from being persisted. --- LICENSE | 0 README.md | 0 SYNQueue.podspec | 0 SYNQueue.xcworkspace/contents.xcworkspacedata | 0 SYNQueue/SYNQueue.xcodeproj/project.pbxproj | 26 +- .../contents.xcworkspacedata | 0 ...24523CEE-5CA3-4AAB-A970-50D68B159D1F.plist | 0 .../Info.plist | 0 .../xcshareddata/xcschemes/SYNQueue.xcscheme | 2 +- .../xcschemes/SYNQueueTests.xcscheme | 2 +- SYNQueue/SYNQueue/Info.plist | 2 +- SYNQueue/SYNQueue/NSDate+Utils.swift | 23 +- SYNQueue/SYNQueue/SYNQueue.h | 0 SYNQueue/SYNQueue/SYNQueue.swift | 49 +-- SYNQueue/SYNQueue/SYNQueueTask.swift | 115 +++---- SYNQueue/SYNQueue/Utils.swift | 27 +- SYNQueue/SYNQueueTests/ConsoleLogger.swift | 8 +- SYNQueue/SYNQueueTests/Info.plist | 2 +- .../NSUserDefaultsSerializer.swift | 18 +- SYNQueue/SYNQueueTests/SYNQueueTests.swift | 40 ++- .../SYNQueueDemo.xcodeproj/project.pbxproj | 30 +- SYNQueueDemo/SYNQueueDemo/AppDelegate.swift | 12 +- .../SYNQueueDemo/Base.lproj/LaunchScreen.xib | 0 SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift | 8 +- .../AppIcon.appiconset/Contents.json | 0 SYNQueueDemo/SYNQueueDemo/Info.plist | 2 +- SYNQueueDemo/SYNQueueDemo/Main.storyboard | 0 .../NSUserDefaultsSerializer.swift | 42 ++- SYNQueueDemo/SYNQueueDemo/Reachability.swift | 298 ++++++++++++++++++ .../SYNQueueDemo/SettingsViewController.swift | 14 +- SYNQueueDemo/SYNQueueDemo/TaskCell.swift | 4 +- SYNQueueDemo/SYNQueueDemo/Utils.swift | 40 ++- .../SYNQueueDemo/ViewController.swift | 81 ++--- SYNQueueDemo/SYNQueueDemoTests/Info.plist | 2 +- .../SYNQueueDemoTests/SYNQueueDemoTests.swift | 2 +- image/logo.png | Bin image/logo.sketch | Bin 37 files changed, 624 insertions(+), 225 deletions(-) mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 SYNQueue.podspec mode change 100644 => 100755 SYNQueue.xcworkspace/contents.xcworkspacedata mode change 100644 => 100755 SYNQueue/SYNQueue.xcodeproj/project.pbxproj mode change 100644 => 100755 SYNQueue/SYNQueue.xcodeproj/project.xcworkspace/contents.xcworkspacedata mode change 100644 => 100755 SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcbaselines/9F6202351B333AAF0026CE2C.xcbaseline/24523CEE-5CA3-4AAB-A970-50D68B159D1F.plist mode change 100644 => 100755 SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcbaselines/9F6202351B333AAF0026CE2C.xcbaseline/Info.plist mode change 100644 => 100755 SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcschemes/SYNQueue.xcscheme mode change 100644 => 100755 SYNQueue/SYNQueue.xcodeproj/xcshareddata/xcschemes/SYNQueueTests.xcscheme mode change 100644 => 100755 SYNQueue/SYNQueue/Info.plist mode change 100644 => 100755 SYNQueue/SYNQueue/NSDate+Utils.swift mode change 100644 => 100755 SYNQueue/SYNQueue/SYNQueue.h mode change 100644 => 100755 SYNQueue/SYNQueue/SYNQueue.swift mode change 100644 => 100755 SYNQueue/SYNQueue/SYNQueueTask.swift mode change 100644 => 100755 SYNQueue/SYNQueue/Utils.swift mode change 100644 => 100755 SYNQueue/SYNQueueTests/ConsoleLogger.swift mode change 100644 => 100755 SYNQueue/SYNQueueTests/Info.plist mode change 100644 => 100755 SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift mode change 100644 => 100755 SYNQueue/SYNQueueTests/SYNQueueTests.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/AppDelegate.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/Base.lproj/LaunchScreen.xib mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/Images.xcassets/AppIcon.appiconset/Contents.json mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/Info.plist mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/Main.storyboard mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift create mode 100644 SYNQueueDemo/SYNQueueDemo/Reachability.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/SettingsViewController.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/TaskCell.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/Utils.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemo/ViewController.swift mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemoTests/Info.plist mode change 100644 => 100755 SYNQueueDemo/SYNQueueDemoTests/SYNQueueDemoTests.swift mode change 100644 => 100755 image/logo.png mode change 100644 => 100755 image/logo.sketch 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..ccdf182 --- 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 /* NSUserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110D91BBA166B003AF0F0 /* NSUserDefaultsSerializer.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 */ @@ -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; }; }; }; @@ -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..a917de3 --- a/SYNQueue/SYNQueue/SYNQueue.swift +++ b/SYNQueue/SYNQueue/SYNQueue.swift @@ -38,27 +38,26 @@ public enum LogLevel: Int { */ @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 */ -@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 deserialzeTasks(_ queue: SYNQueue) -> [SYNQueueTask] + func removeTask(_ taskID: String, queue: SYNQueue) } /** * 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? @@ -66,7 +65,7 @@ public class SYNQueue : NSOperationQueue { var taskHandlers = [String: SYNTaskCallback]() let completionBlock: SYNTaskCompleteCallback? - public var tasks: [SYNQueueTask] { + open var tasks: [SYNQueueTask] { let array = operations var output = [SYNQueueTask]() @@ -113,25 +112,27 @@ public class SYNQueue : NSOperationQueue { - 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) { + open func addTaskHandler(_ taskType: String, taskHandler:@escaping SYNTaskCallback) { taskHandlers[taskType] = taskHandler } /** Deserializes tasks that were serialized (persisted) */ - public func loadSerializedTasks() { + open func loadSerializedTasks() { + self.pause() if let sp = serializationProvider { - let tasks = sp.deserializeTasksInQueue(self) - + let tasks = sp.deserialzeTasks(self) + print("got \(tasks.count) deserialized tasks") for task in tasks { task.setupDependencies(tasks) addDeserializedTask(task) } } + self.start() } - public func getTask(taskID: String) -> SYNQueueTask? { + open func getTask(_ taskID: String) -> SYNQueueTask? { return tasksMap[taskID] } @@ -140,7 +141,7 @@ public class SYNQueue : NSOperationQueue { - parameter op: A SYNQueueTask to execute on the queue */ - override public func addOperation(op: NSOperation) { + override open func addOperation(_ op: Operation) { if let task = op as? SYNQueueTask { if tasksMap[task.taskID] != nil { log(.Warning, "Attempted to add duplicate task \(task.taskID)") @@ -155,10 +156,18 @@ public class SYNQueue : NSOperationQueue { } op.completionBlock = { self.taskComplete(op) } - super.addOperation(op) + super.addOperation(op) + } + + open func start() { + self.isSuspended = false + } + + open func pause() { + self.isSuspended = true } - func addDeserializedTask(task: SYNQueueTask) { + func addDeserializedTask(_ task: SYNQueueTask) { if tasksMap[task.taskID] != nil { log(.Warning, "Attempted to add duplicate deserialized task \(task.taskID)") return @@ -168,7 +177,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,9 +186,9 @@ public class SYNQueue : NSOperationQueue { } } - func taskComplete(op: NSOperation) { + func taskComplete(_ op: Operation) { if let task = op as? SYNQueueTask { - tasksMap.removeValueForKey(task.taskID) + tasksMap.removeValue(forKey: task.taskID) if let handler = completionBlock { handler(task.lastError, task) @@ -192,7 +201,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..4fcd2ce --- a/SYNQueue/SYNQueue/SYNQueueTask.swift +++ b/SYNQueue/SYNQueue/SYNQueueTask.swift @@ -7,46 +7,46 @@ import Foundation public typealias SYNTaskCallback = (SYNQueueTask) -> Void public typealias SYNTaskCompleteCallback = (NSError?, SYNQueueTask) -> Void -public typealias JSONDictionary = [String: AnyObject?] +public typealias JSONDictionary = [String: Any?] /** * 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 started: 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") } } @@ -67,14 +67,14 @@ public class SYNQueueTask : NSOperation { - 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) + public init(queue: SYNQueue, taskID: String? = nil, taskType: String, + dependencyStrs: [String] = [], data: Any? = nil, + created: Date = Date(), started: Date? = nil, retries: Int = 0, + queuePriority: Operation.QueuePriority = .normal, + qualityOfService: QualityOfService = .utility) { self.queue = queue - self.taskID = taskID ?? NSUUID().UUIDString + self.taskID = taskID ?? UUID().uuidString self.taskType = taskType self.dependencyStrs = dependencyStrs self.data = data @@ -100,7 +100,7 @@ public class SYNQueueTask : NSOperation { - returns: A new SYNQueueTask */ - public convenience init(queue: SYNQueue, type: String, data: AnyObject? = nil, retries: Int = 0, priority: NSOperationQueuePriority = .Normal, quality: NSQualityOfService = .Utility) { + public convenience init(queue: SYNQueue, type: String, data: Any? = nil, retries: Int = 0, priority: Operation.QueuePriority = .normal, quality: QualityOfService = .utility) { self.init(queue: queue, taskType: type, data: data, retries: retries, queuePriority: priority, qualityOfService: quality) } @@ -124,19 +124,20 @@ public class SYNQueueTask : NSOperation { 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 data: Any? = dictionary["data"] as Any??, let createdStr = dictionary["created"] as? String, - let startedStr: String? = dictionary["started"] as? String ?? nil, + //let startedStr = dictionary["started"] as? String, 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 + let created = Date(dateString: createdStr) ?? Date() + //let started = Date(dateString: startedStr) + + let priority = Operation.QueuePriority(rawValue: queuePriority) ?? .normal + let qos = QualityOfService(rawValue: qualityOfService) ?? .utility self.init(queue: queue, taskID: taskID, taskType: taskType, dependencyStrs: dependencyStrs, data: data, created: created, - started: started, retries: retries, queuePriority: priority, + started: nil, retries: retries, queuePriority: priority, qualityOfService: qos) } else { self.init(queue: queue, taskID: "", taskType: "") @@ -154,7 +155,7 @@ public class SYNQueueTask : NSOperation { */ 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 @@ -169,7 +170,7 @@ public class SYNQueueTask : NSOperation { - parameter allTasks: Array of SYNQueueTasks that are dependencies of this task */ - public func setupDependencies(allTasks: [SYNQueueTask]) { + open func setupDependencies(_ allTasks: [SYNQueueTask]) { dependencyStrs.forEach { (taskID: String) -> Void in @@ -188,17 +189,20 @@ public class SYNQueueTask : NSOperation { - 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 + open func toDictionary() -> [String: Any?] { + var dict = [String: Any?]() + dict["taskID"] = self.taskID as Any + dict["taskType"] = self.taskType as Any + dict["dependencies"] = self.dependencyStrs as Any + dict["queuePriority"] = self.queuePriority.rawValue as Any + dict["qualityOfService"] = self.qualityOfService.rawValue as Any dict["data"] = self.data dict["created"] = self.created.toISOString() - dict["started"] = (self.started != nil) ? self.started!.toISOString() : nil - dict["retries"] = self.retries + //dict["started"] = (self.started != nil) ? self.started!.toISOString() : nil + if let started = self.started { + dict["started"] = started.toISOString() + } + dict["retries"] = self.retries as Any return dict } @@ -208,7 +212,7 @@ public class SYNQueueTask : NSOperation { - returns: A JSON string representation of the task */ - public func toJSONString() -> String? { + open func toJSONString() -> String? { // Serialize this task to a dictionary let dict = toDictionary() @@ -230,28 +234,28 @@ public class SYNQueueTask : NSOperation { /** Starts executing the task */ - public override func start() { + open override func start() { super.start() - executing = true + isExecuting = true run() } /** Cancels the task */ - public override func cancel() { + 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) } @@ -262,10 +266,10 @@ public class SYNQueueTask : NSOperation { - parameter error: If the task failed, pass an error to indicate why */ - public func completed(error: NSError?) { + 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 +279,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..c0b2f51 --- a/SYNQueue/SYNQueue/Utils.swift +++ b/SYNQueue/SYNQueue/Utils.swift @@ -5,31 +5,30 @@ 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: () -> ()) { +func synced(_ lock: Any, closure: () -> ()) { objc_sync_enter(lock) closure() objc_sync_exit(lock) } -func runOnMainThread(callback:dispatch_block_t) { - dispatch_async(dispatch_get_main_queue(), callback) +func runOnMainThread(_ callback:@escaping ()->()) { + DispatchQueue.main.async(execute: callback) } -func toJSON(obj: AnyObject) throws -> String? { - let json = try NSJSONSerialization.dataWithJSONObject(obj, options: []) - return NSString(data: json, encoding: NSUTF8StringEncoding) as String? +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 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..8629bcb --- a/SYNQueue/SYNQueueTests/ConsoleLogger.swift +++ b/SYNQueue/SYNQueueTests/ConsoleLogger.swift @@ -6,7 +6,7 @@ import Foundation import SYNQueue -func log(level: LogLevel, _ msg: String) { +func log(_ level: LogLevel, _ msg: String) { return ConsoleLogger.log(level, msg) } @@ -14,11 +14,11 @@ func log(level: LogLevel, _ msg: String) { class ConsoleLogger : SYNQueueLogProvider { // MARK: - SYNQueueLogProvider Delegates - @objc func log(level: LogLevel, _ msg: String) { + @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)") } + class func log(_ level: LogLevel, _ msg: String) { + runOnMainThread { print("[\(level.toString().uppercased())] \(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/NSUserDefaultsSerializer.swift b/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift old mode 100644 new mode 100755 index 24a2a46..89def87 --- a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift +++ b/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift @@ -10,12 +10,12 @@ import SYNQueue class NSUserDefaultsSerializer : SYNQueueSerializationProvider { // MARK: - SYNQueueSerializationProvider Methods - @objc func serializeTask(task: SYNQueueTask, queueName: String) { + @objc 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 { @@ -24,14 +24,14 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { defaults.setValue(stringArray, forKey: queueName) } else { - log(.Error, "Failed to serialize task \(task.taskID) in queue \(queueName)") + log(.error, "Failed to serialize task \(task.taskID) in queue \(queueName)") } } - @objc func deserializeTasksInQueue(queue: SYNQueue) -> [SYNQueueTask] { - let defaults = NSUserDefaults.standardUserDefaults() + @objc func deserializeTasksInQueue(_ 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) } @@ -42,7 +42,7 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { return [] } - @objc func removeTask(taskID: String, queue: SYNQueue) { + @objc func removeTask(_ taskID: String, queue: SYNQueue) { if let queueName = queue.name { var curArray: [SYNQueueTask] = deserializeTasksInQueue(queue) curArray = curArray.filter { return $0.taskID != taskID } @@ -52,7 +52,7 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { .filter { return $0 != nil } .map { return $0! } - let defaults = NSUserDefaults.standardUserDefaults() + let defaults = UserDefaults.standard defaults.setValue(stringArray, forKey: queueName) } } diff --git a/SYNQueue/SYNQueueTests/SYNQueueTests.swift b/SYNQueue/SYNQueueTests/SYNQueueTests.swift old mode 100644 new mode 100755 index 5baa310..4a115de --- a/SYNQueue/SYNQueueTests/SYNQueueTests.swift +++ b/SYNQueue/SYNQueueTests/SYNQueueTests.swift @@ -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) @@ -98,18 +98,46 @@ 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) } } + + func testInternetDependency() { + let name = randomQueueName() + + let queue = SYNQueue(queueName: name, maxConcurrency: 3, maxRetries: 2, logProvider: logger, serializationProvider: serializer) { (error: NSError?, task: SYNQueueTask) -> Void in + // + } + + queue.addTaskHandler(testTaskType) { + NSThread.sleepForTimeInterval(1) + $0.completed(nil) + } + + queue.addTaskHandler("internetDependencyType") { task in + let reachability = Reachability.reachabilityForInternetConnection() + reachability?.whenReachable = { reachability in + task.completed(nil) + } + } + + let task = SYNQueueTask(queue: queue, taskType: testTaskType) + let internetDependency = SYNQueueTask(queue: queue, type: "internetDependencyType", retries: 0) + task.addDependency(internetDependency) + queue.addOperation(task) + + } } // MARK: Helper methods func randomQueueName() -> String { - return NSUUID().UUIDString + return UUID().uuidString } + + diff --git a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index c47672c..fba9d40 --- a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj +++ b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ 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 */; }; @@ -18,8 +19,8 @@ 9FD63FC51B338BA5001BD09A /* TaskCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FC41B338BA5001BD09A /* TaskCell.swift */; }; 9FD63FC71B33EA2F001BD09A /* NSUserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FC61B33EA2F001BD09A /* NSUserDefaultsSerializer.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 */ @@ -47,6 +48,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 = ""; }; @@ -109,6 +111,7 @@ 9FD63F9D1B333BDC001BD09A /* Images.xcassets */, 9FD63F941B333BDC001BD09A /* Supporting Files */, 9FD63F961B333BDC001BD09A /* AppDelegate.swift */, + 0EB0B1B21EE7E85E00B43FB0 /* Reachability.swift */, 9F70B5021B368AB500BDB945 /* ConsoleLogger.swift */, 9FD63F981B333BDC001BD09A /* ViewController.swift */, DC67E47F1BB0F1BD00F3D9E2 /* SettingsViewController.swift */, @@ -193,15 +196,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,6 +261,7 @@ 9FD63FC51B338BA5001BD09A /* TaskCell.swift in Sources */, 9F70B5031B368AB500BDB945 /* ConsoleLogger.swift in Sources */, 9FD63F971B333BDC001BD09A /* AppDelegate.swift in Sources */, + 0EB0B1B31EE7E85E00B43FB0 /* Reachability.swift in Sources */, 9FD63FC71B33EA2F001BD09A /* NSUserDefaultsSerializer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -303,14 +309,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 +357,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 +379,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 +388,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 +427,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 +440,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/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..8629bcb --- a/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift +++ b/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift @@ -6,7 +6,7 @@ import Foundation import SYNQueue -func log(level: LogLevel, _ msg: String) { +func log(_ level: LogLevel, _ msg: String) { return ConsoleLogger.log(level, msg) } @@ -14,11 +14,11 @@ func log(level: LogLevel, _ msg: String) { class ConsoleLogger : SYNQueueLogProvider { // MARK: - SYNQueueLogProvider Delegates - @objc func log(level: LogLevel, _ msg: String) { + @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)") } + class func log(_ level: LogLevel, _ msg: String) { + runOnMainThread { print("[\(level.toString().uppercased())] \(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/NSUserDefaultsSerializer.swift b/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift old mode 100644 new mode 100755 index 24a2a46..c3c9215 --- a/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift +++ b/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift @@ -6,54 +6,50 @@ import Foundation import SYNQueue - -class NSUserDefaultsSerializer : SYNQueueSerializationProvider { - // MARK: - SYNQueueSerializationProvider Methods +public class NSUserDefaultsSerializer: SYNQueueSerializationProvider { + + public init() { } - @objc func serializeTask(task: SYNQueueTask, queueName: String) { + public 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 { stringArray = [serialized] } - defaults.setValue(stringArray, forKey: queueName) + defaults.synchronize() } else { log(.Error, "Failed to serialize task \(task.taskID) in queue \(queueName)") } } - @objc func deserializeTasksInQueue(queue: SYNQueue) -> [SYNQueueTask] { - let defaults = NSUserDefaults.standardUserDefaults() - if let queueName = queue.name, - let stringArray = defaults.stringArrayForKey(queueName) - { + public func deserialzeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { + let defaults = UserDefaults.standard + if let queneName = queue.name, + let stringArray = defaults.stringArray(forKey: queneName) { + return stringArray - .map { return SYNQueueTask(json: $0, queue: queue) } + .map { return SYNQueueTask(json: $0, queue: queue)} .filter { return $0 != nil } .map { return $0! } } - return [] } - @objc func removeTask(taskID: String, queue: SYNQueue) { + public func removeTask(_ taskID: String, queue: SYNQueue) { if let queueName = queue.name { - var curArray: [SYNQueueTask] = deserializeTasksInQueue(queue) - curArray = curArray.filter { return $0.taskID != taskID } - + var curArray: [SYNQueueTask] = deserialzeTasks(queue) + curArray = curArray.filter {return $0.taskID != taskID } let stringArray = curArray - .map { return $0.toJSONString() } - .filter { return $0 != nil } + .map {return $0.toJSONString() } + .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/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/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..baf85c6 --- 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: NSUserDefaultsSerializer(), + completionBlock: { [weak self] in self?.taskComplete($0, $1) }) }() // MARK: - UIViewController Overrides @@ -26,25 +31,27 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection queue.addTaskHandler("cellTask", taskHandler: taskHandler) queue.loadSerializedTasks() + print("pre-loaded tasks: \(UserDefaults.standard.stringArray(forKey: "myQueue") ?? [])") + 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 +62,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 +71,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 { @@ -86,31 +92,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,16 +123,18 @@ 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 taskID1 = nextTaskID + nextTaskID += 1 + let task1 = SYNQueueTask(queue: queue, taskID: String(describing: taskID1), + taskType: "cellTask", dependencyStrs: [], data: [:]) - let shouldAddDependency = NSUserDefaults.standardUserDefaults().boolForKey(kAddDependencySettingKey) + let shouldAddDependency = UserDefaults.standard.bool(forKey: kAddDependencySettingKey) if shouldAddDependency { - let taskID2 = nextTaskID++ - let task2 = SYNQueueTask(queue: queue, taskID: String(taskID2), - taskType: "cellTask", dependencyStrs: [], data: [:]) + let taskID2 = nextTaskID + nextTaskID += 1 + let task2 = SYNQueueTask(queue: queue, taskID: String(describing: taskID2), + taskType: "cellTask", dependencyStrs: [], data: [:]) // Make the first task dependent on the second task1.addDependency(task2) @@ -142,13 +148,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 +162,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 From 5985268b22d364a6bbbf272940005cab6c6047bb Mon Sep 17 00:00:00 2001 From: Rajavelu Chandrasekaran Date: Thu, 8 Jun 2017 15:55:27 +0800 Subject: [PATCH 2/6] Fixed framework compiler errors in SYNQueueTests" --- SYNQueue/SYNQueue/SYNQueue.swift | 4 +-- SYNQueue/SYNQueue/Utils.swift | 6 ---- .../NSUserDefaultsSerializer.swift | 10 +++--- SYNQueue/SYNQueueTests/SYNQueueTests.swift | 34 +++---------------- .../SYNQueueDemo.xcodeproj/project.pbxproj | 6 ---- .../contents.xcworkspacedata | 7 ++++ 6 files changed, 18 insertions(+), 49 deletions(-) create mode 100644 SYNQueueDemo/SYNQueueDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/SYNQueue/SYNQueue/SYNQueue.swift b/SYNQueue/SYNQueue/SYNQueue.swift index a917de3..6b71976 100755 --- a/SYNQueue/SYNQueue/SYNQueue.swift +++ b/SYNQueue/SYNQueue/SYNQueue.swift @@ -46,7 +46,7 @@ public protocol SYNQueueLogProvider { */ public protocol SYNQueueSerializationProvider { func serializeTask(_ task: SYNQueueTask, queueName: String) - func deserialzeTasks(_ queue: SYNQueue) -> [SYNQueueTask] + func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] func removeTask(_ taskID: String, queue: SYNQueue) } @@ -122,7 +122,7 @@ open class SYNQueue : OperationQueue { open func loadSerializedTasks() { self.pause() if let sp = serializationProvider { - let tasks = sp.deserialzeTasks(self) + let tasks = sp.deserializeTasks(self) print("got \(tasks.count) deserialized tasks") for task in tasks { task.setupDependencies(tasks) diff --git a/SYNQueue/SYNQueue/Utils.swift b/SYNQueue/SYNQueue/Utils.swift index c0b2f51..935ed6c 100755 --- a/SYNQueue/SYNQueue/Utils.swift +++ b/SYNQueue/SYNQueue/Utils.swift @@ -10,12 +10,6 @@ func runInBackgroundAfter(_ seconds: TimeInterval, callback:@escaping ()->()) { DispatchQueue.global(qos: DispatchQoS.QoSClass.background).asyncAfter(deadline: delta, execute: callback) } -func synced(_ lock: Any, closure: () -> ()) { - objc_sync_enter(lock) - closure() - objc_sync_exit(lock) -} - func runOnMainThread(_ callback:@escaping ()->()) { DispatchQueue.main.async(execute: callback) } diff --git a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift b/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift index 89def87..b8e74e1 100755 --- a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift +++ b/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift @@ -10,7 +10,7 @@ import SYNQueue class NSUserDefaultsSerializer : SYNQueueSerializationProvider { // MARK: - SYNQueueSerializationProvider Methods - @objc func serializeTask(_ task: SYNQueueTask, queueName: String) { + func serializeTask(_ task: SYNQueueTask, queueName: String) { if let serialized = task.toJSONString() { let defaults = UserDefaults.standard var stringArray: [String] @@ -24,11 +24,11 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { defaults.setValue(stringArray, forKey: queueName) } else { - log(.error, "Failed to serialize task \(task.taskID) in queue \(queueName)") + log(.Error, "Failed to serialize task \(task.taskID) in queue \(queueName)") } } - @objc func deserializeTasksInQueue(_ queue: SYNQueue) -> [SYNQueueTask] { + func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { let defaults = UserDefaults.standard if let queueName = queue.name, let stringArray = defaults.stringArray(forKey: queueName) @@ -42,9 +42,9 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { 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 diff --git a/SYNQueue/SYNQueueTests/SYNQueueTests.swift b/SYNQueue/SYNQueueTests/SYNQueueTests.swift index 4a115de..440fe09 100755 --- a/SYNQueue/SYNQueueTests/SYNQueueTests.swift +++ b/SYNQueue/SYNQueueTests/SYNQueueTests.swift @@ -50,7 +50,7 @@ class SYNQueueTests: XCTestCase { } queue.addTaskHandler(testTaskType) { $0.completed(nil) } - let task = SYNQueueTask(queue: queue, taskType: testTaskType) + let task = SYNQueueTask(queue: queue, taskType: testTaskType, data: nil) queue.addOperation(task) XCTAssert(queue.operationCount == 1) @@ -73,7 +73,7 @@ class SYNQueueTests: XCTestCase { Thread.sleep(forTimeInterval: 2) $0.completed(nil) } - let task = SYNQueueTask(queue: queue!, taskType: testTaskType) + let task = SYNQueueTask(queue: queue!, taskType: testTaskType, data: nil) queue!.addOperation(task) // Nil out the queue to simulate app backgrounded or quit @@ -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) }) @@ -101,38 +101,12 @@ class SYNQueueTests: XCTestCase { Thread.sleep(forTimeInterval: 1) $0.completed(nil) } - let task = SYNQueueTask(queue: queue, taskType: testTaskType) + let task = SYNQueueTask(queue: queue, taskType: testTaskType, data: nil) self.measure() { queue.addOperation(task) } } - - func testInternetDependency() { - let name = randomQueueName() - - let queue = SYNQueue(queueName: name, maxConcurrency: 3, maxRetries: 2, logProvider: logger, serializationProvider: serializer) { (error: NSError?, task: SYNQueueTask) -> Void in - // - } - - queue.addTaskHandler(testTaskType) { - NSThread.sleepForTimeInterval(1) - $0.completed(nil) - } - - queue.addTaskHandler("internetDependencyType") { task in - let reachability = Reachability.reachabilityForInternetConnection() - reachability?.whenReachable = { reachability in - task.completed(nil) - } - } - - let task = SYNQueueTask(queue: queue, taskType: testTaskType) - let internetDependency = SYNQueueTask(queue: queue, type: "internetDependencyType", retries: 0) - task.addDependency(internetDependency) - queue.addOperation(task) - - } } // MARK: Helper methods diff --git a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj index fba9d40..ff8d4d3 100755 --- a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj +++ b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj @@ -14,8 +14,6 @@ 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 */; }; 9FD63FCD1B3417DF001BD09A /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FCC1B3417DF001BD09A /* Utils.swift */; }; @@ -40,7 +38,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 9FD63FBA1B333E15001BD09A /* SYNQueue.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -59,7 +56,6 @@ 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 = ""; }; 9FD63FCC1B3417DF001BD09A /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; @@ -72,7 +68,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9FD63FB91B333E15001BD09A /* SYNQueue.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -89,7 +84,6 @@ 9FD63F881B333BDC001BD09A = { isa = PBXGroup; children = ( - 9FD63FB81B333E15001BD09A /* SYNQueue.framework */, 9FD63F931B333BDC001BD09A /* SYNQueueDemo */, 9FD63FA91B333BDC001BD09A /* SYNQueueDemoTests */, 9FD63F921B333BDC001BD09A /* Products */, 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 @@ + + + + + From e8a9d654da3637d93eda58a239e88f9030590eb0 Mon Sep 17 00:00:00 2001 From: Rajavelu Chandrasekaran Date: Thu, 8 Jun 2017 16:09:28 +0800 Subject: [PATCH 3/6] Fixed erroneous typos in UserDefaultsSerializer Please enter the commit message for your changes. Lines starting --- SYNQueue/SYNQueue.xcodeproj/project.pbxproj | 8 ++--- SYNQueue/SYNQueueTests/SYNQueueTests.swift | 4 +-- .../UserDefaultsSerializer.swift | 31 ++++++++++--------- .../SYNQueueDemo.xcodeproj/project.pbxproj | 8 ++--- .../SYNQueueDemo/UserDefaultsSerializer.swift | 12 +++---- .../SYNQueueDemo/ViewController.swift | 2 +- 6 files changed, 32 insertions(+), 33 deletions(-) rename SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift => SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift (63%) rename SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift => SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift (89%) diff --git a/SYNQueue/SYNQueue.xcodeproj/project.pbxproj b/SYNQueue/SYNQueue.xcodeproj/project.pbxproj index ccdf182..08cb928 100755 --- a/SYNQueue/SYNQueue.xcodeproj/project.pbxproj +++ b/SYNQueue/SYNQueue.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ 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 */; }; - DCD110DB1BBA166B003AF0F0 /* NSUserDefaultsSerializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110D91BBA166B003AF0F0 /* NSUserDefaultsSerializer.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 */; }; DCD110E31BBA2FBC003AF0F0 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD110E11BBA2FB0003AF0F0 /* Utils.swift */; }; @@ -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"; @@ -244,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 */, ); diff --git a/SYNQueue/SYNQueueTests/SYNQueueTests.swift b/SYNQueue/SYNQueueTests/SYNQueueTests.swift index 440fe09..b0da733 100755 --- 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() } diff --git a/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift b/SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift similarity index 63% rename from SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift rename to SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift index c3c9215..b02b626 100755 --- a/SYNQueueDemo/SYNQueueDemo/NSUserDefaultsSerializer.swift +++ b/SYNQueue/SYNQueueTests/UserDefaultsSerializer.swift @@ -6,11 +6,10 @@ import Foundation import SYNQueue -public class NSUserDefaultsSerializer: SYNQueueSerializationProvider { +class UserDefaultsSerializer: SYNQueueSerializationProvider { + // MARK: - SYNQueueSerializationProvider Methods - public init() { } - - public func serializeTask(_ task: SYNQueueTask, queueName: String) { + func serializeTask(_ task: SYNQueueTask, queueName: String) { if let serialized = task.toJSONString() { let defaults = UserDefaults.standard var stringArray: [String] @@ -21,34 +20,36 @@ public class NSUserDefaultsSerializer: SYNQueueSerializationProvider { } else { stringArray = [serialized] } + defaults.setValue(stringArray, forKey: queueName) - defaults.synchronize() } else { log(.Error, "Failed to serialize task \(task.taskID) in queue \(queueName)") } } - public func deserialzeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { + func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { let defaults = UserDefaults.standard - if let queneName = queue.name, - let stringArray = defaults.stringArray(forKey: queneName) { - + if let queueName = queue.name, + let stringArray = defaults.stringArray(forKey: queueName) { + return stringArray - .map { return SYNQueueTask(json: $0, queue: queue)} + .map { return SYNQueueTask(json: $0, queue: queue) } .filter { return $0 != nil } .map { return $0! } } return [] } - public func removeTask(_ taskID: String, queue: SYNQueue) { + func removeTask(_ taskID: String, queue: SYNQueue) { if let queueName = queue.name { - var curArray: [SYNQueueTask] = deserialzeTasks(queue) - curArray = curArray.filter {return $0.taskID != taskID } + var curArray: [SYNQueueTask] = deserializeTasks(queue) + curArray = curArray.filter { return $0.taskID != taskID } + let stringArray = curArray - .map {return $0.toJSONString() } - .filter { return $0 != nil} + .map { return $0.toJSONString() } + .filter { return $0 != nil } .map { return $0! } + UserDefaults.standard.setValue(stringArray, forKey: queueName) } } diff --git a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj index ff8d4d3..6a2ac1f 100755 --- a/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj +++ b/SYNQueueDemo/SYNQueueDemo.xcodeproj/project.pbxproj @@ -15,7 +15,7 @@ 9FD63FA11B333BDC001BD09A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9FD63F9F1B333BDC001BD09A /* LaunchScreen.xib */; }; 9FD63FAD1B333BDC001BD09A /* SYNQueueDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD63FAC1B333BDC001BD09A /* SYNQueueDemoTests.swift */; }; 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 */; }; DC67E4801BB0F1BD00F3D9E2 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC67E47F1BB0F1BD00F3D9E2 /* SettingsViewController.swift */; }; @@ -57,7 +57,7 @@ 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 = ""; }; 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 = ""; }; @@ -112,7 +112,7 @@ DC67E47D1BB0F01200F3D9E2 /* Main.storyboard */, 9FD63F9F1B333BDC001BD09A /* LaunchScreen.xib */, 9FD63FC41B338BA5001BD09A /* TaskCell.swift */, - 9FD63FC61B33EA2F001BD09A /* NSUserDefaultsSerializer.swift */, + 9FD63FC61B33EA2F001BD09A /* UserDefaultsSerializer.swift */, 9FD63FCC1B3417DF001BD09A /* Utils.swift */, ); path = SYNQueueDemo; @@ -256,7 +256,7 @@ 9F70B5031B368AB500BDB945 /* ConsoleLogger.swift in Sources */, 9FD63F971B333BDC001BD09A /* AppDelegate.swift in Sources */, 0EB0B1B31EE7E85E00B43FB0 /* Reachability.swift in Sources */, - 9FD63FC71B33EA2F001BD09A /* NSUserDefaultsSerializer.swift in Sources */, + 9FD63FC71B33EA2F001BD09A /* UserDefaultsSerializer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift b/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift similarity index 89% rename from SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift rename to SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift index b8e74e1..a1ae7bb 100755 --- a/SYNQueue/SYNQueueTests/NSUserDefaultsSerializer.swift +++ b/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift @@ -6,8 +6,7 @@ import Foundation import SYNQueue - -class NSUserDefaultsSerializer : SYNQueueSerializationProvider { +class UserDefaultsSerializer : SYNQueueSerializationProvider { // MARK: - SYNQueueSerializationProvider Methods func serializeTask(_ task: SYNQueueTask, queueName: String) { @@ -31,8 +30,8 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { let defaults = UserDefaults.standard if let queueName = queue.name, - let stringArray = defaults.stringArray(forKey: queueName) - { + let stringArray = defaults.stringArray(forKey: queueName) { + return stringArray .map { return SYNQueueTask(json: $0, queue: queue) } .filter { return $0 != nil } @@ -51,9 +50,8 @@ class NSUserDefaultsSerializer : SYNQueueSerializationProvider { .map { return $0.toJSONString() } .filter { return $0 != nil } .map { return $0! } - - let defaults = UserDefaults.standard - defaults.setValue(stringArray, forKey: queueName) + + UserDefaults.standard.setValue(stringArray, forKey: queueName) } } } diff --git a/SYNQueueDemo/SYNQueueDemo/ViewController.swift b/SYNQueueDemo/SYNQueueDemo/ViewController.swift index baf85c6..42d8102 100755 --- a/SYNQueueDemo/SYNQueueDemo/ViewController.swift +++ b/SYNQueueDemo/SYNQueueDemo/ViewController.swift @@ -19,7 +19,7 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection maxConcurrency: 2, maxRetries: 3, logProvider: ConsoleLogger(), - serializationProvider: NSUserDefaultsSerializer(), + serializationProvider: UserDefaultsSerializer(), completionBlock: { [weak self] in self?.taskComplete($0, $1) }) }() From a3a073011a0e161d595e68abe722c70e32ad6c95 Mon Sep 17 00:00:00 2001 From: Rajavelu Chandrasekaran Date: Thu, 8 Jun 2017 16:21:08 +0800 Subject: [PATCH 4/6] Cleaned up ConsoleLogger --- SYNQueue/SYNQueue/SYNQueue.swift | 1 + SYNQueue/SYNQueueTests/ConsoleLogger.swift | 17 +++++++---------- SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift | 16 ++++++---------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/SYNQueue/SYNQueue/SYNQueue.swift b/SYNQueue/SYNQueue/SYNQueue.swift index 6b71976..285338f 100755 --- a/SYNQueue/SYNQueue/SYNQueue.swift +++ b/SYNQueue/SYNQueue/SYNQueue.swift @@ -44,6 +44,7 @@ public protocol SYNQueueLogProvider { /** * Conform to this protocol to provide serialization (persistence) to SYNQueue */ +@objc public protocol SYNQueueSerializationProvider { func serializeTask(_ task: SYNQueueTask, queueName: String) func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] diff --git a/SYNQueue/SYNQueueTests/ConsoleLogger.swift b/SYNQueue/SYNQueueTests/ConsoleLogger.swift index 8629bcb..58c13c7 100755 --- a/SYNQueue/SYNQueueTests/ConsoleLogger.swift +++ b/SYNQueue/SYNQueueTests/ConsoleLogger.swift @@ -6,19 +6,16 @@ import Foundation import SYNQueue +import Foundation + func log(_ level: LogLevel, _ msg: String) { - return ConsoleLogger.log(level, msg) + 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().uppercased())] \(msg)") } + public func log(_ level: LogLevel, _ msg: String) { + print("[\(level.toString())] \(msg)") } } + diff --git a/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift b/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift index 8629bcb..fdc9b96 100755 --- a/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift +++ b/SYNQueueDemo/SYNQueueDemo/ConsoleLogger.swift @@ -6,19 +6,15 @@ import Foundation import SYNQueue +import Foundation + func log(_ level: LogLevel, _ msg: String) { - return ConsoleLogger.log(level, msg) + 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().uppercased())] \(msg)") } + public func log(_ level: LogLevel, _ msg: String) { + print("[\(level.toString())] \(msg)") } } From c92b754fb8a9d38aa803d8edc3c6f72ae10ad77b Mon Sep 17 00:00:00 2001 From: Rajavelu Chandrasekaran Date: Thu, 8 Jun 2017 18:42:19 +0800 Subject: [PATCH 5/6] Revised the task initializers to generate built it taskID, createdDate, queuePriority. Removed dependencyStrs and let the operation handle the dependency by itself. --- SYNQueue/SYNQueue/SYNQueue.swift | 114 +++++----- SYNQueue/SYNQueue/SYNQueueTask.swift | 202 +++++++----------- SYNQueue/SYNQueueTests/SYNQueueTests.swift | 6 +- .../SYNQueueDemo/UserDefaultsSerializer.swift | 17 +- .../SYNQueueDemo/ViewController.swift | 25 +-- 5 files changed, 151 insertions(+), 213 deletions(-) diff --git a/SYNQueue/SYNQueue/SYNQueue.swift b/SYNQueue/SYNQueue/SYNQueue.swift index 285338f..e1c10e6 100755 --- 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,26 +24,26 @@ 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) } /** -* 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) @@ -52,8 +52,8 @@ public protocol SYNQueueSerializationProvider { } /** -* 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 open class SYNQueue : OperationQueue { @@ -62,7 +62,7 @@ open class SYNQueue : OperationQueue { let serializationProvider: SYNQueueSerializationProvider? let logProvider: SYNQueueLogProvider? - var tasksMap = [String: SYNQueueTask]() + var taskMap = [String: SYNQueueTask]() var taskHandlers = [String: SYNTaskCallback]() let completionBlock: SYNTaskCompleteCallback? @@ -80,22 +80,24 @@ open class SYNQueue : OperationQueue { } /** - 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,25 +110,23 @@ open class SYNQueue : OperationQueue { } /** - 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) { + 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) - */ + Deserializes tasks that were serialized (persisted) + */ open func loadSerializedTasks() { self.pause() if let sp = serializationProvider { let tasks = sp.deserializeTasks(self) - print("got \(tasks.count) deserialized tasks") for task in tasks { - task.setupDependencies(tasks) addDeserializedTask(task) } } @@ -134,21 +134,21 @@ open class SYNQueue : OperationQueue { } open func getTask(_ taskID: String) -> SYNQueueTask? { - return tasksMap[taskID] + return taskMap[taskID] } /** - Adds a SYNQueueTask to the queue and serializes it - - - parameter op: A SYNQueueTask to execute on the queue - */ + 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 { @@ -157,7 +157,7 @@ open class SYNQueue : OperationQueue { } op.completionBlock = { self.taskComplete(op) } - super.addOperation(op) + super.addOperation(op) } open func start() { @@ -169,7 +169,7 @@ open class SYNQueue : OperationQueue { } func addDeserializedTask(_ task: SYNQueueTask) { - if tasksMap[task.taskID] != nil { + if taskMap[task.taskID] != nil { log(.Warning, "Attempted to add duplicate deserialized task \(task.taskID)") return } @@ -189,8 +189,8 @@ open class SYNQueue : OperationQueue { func taskComplete(_ op: Operation) { if let task = op as? SYNQueueTask { - tasksMap.removeValue(forKey: task.taskID) - + taskMap.removeValue(forKey: task.taskID) + if let handler = completionBlock { handler(task.lastError, task) } diff --git a/SYNQueue/SYNQueue/SYNQueueTask.swift b/SYNQueue/SYNQueue/SYNQueueTask.swift index 4fcd2ce..f11bc64 100755 --- a/SYNQueue/SYNQueue/SYNQueueTask.swift +++ b/SYNQueue/SYNQueue/SYNQueueTask.swift @@ -9,9 +9,11 @@ public typealias SYNTaskCallback = (SYNQueueTask) -> Void public typealias SYNTaskCompleteCallback = (NSError?, SYNQueueTask) -> Void public typealias JSONDictionary = [String: Any?] +private let MAX_RETRY_LIMIT = 3 + /** -* Represents a task to be executed on a SYNQueue -*/ + * Represents a task to be executed on a SYNQueue + */ @objc open class SYNQueueTask : Operation { static let MIN_RETRY_DELAY = 0.2 @@ -22,10 +24,8 @@ open class SYNQueueTask : Operation { open let taskType: String open let data: Any? open let created: Date - open var started: Date? open var retries: Int - let dependencyStrs: [String] var lastError: NSError? var _executing: Bool = false var _finished: Bool = false @@ -51,108 +51,88 @@ open class SYNQueueTask : Operation { } /** - 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 - */ - public init(queue: SYNQueue, taskID: String? = nil, taskType: String, - dependencyStrs: [String] = [], data: Any? = nil, - created: Date = Date(), started: Date? = nil, retries: Int = 0, - queuePriority: Operation.QueuePriority = .normal, - qualityOfService: QualityOfService = .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 ?? UUID().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: Any? = nil, retries: Int = 0, priority: Operation.QueuePriority = .normal, quality: QualityOfService = .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) { + public 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: Any? = dictionary["data"] as Any??, let createdStr = dictionary["created"] as? String, - //let startedStr = dictionary["started"] as? String, - let retries = dictionary["retries"] as? Int? ?? 0 - { - let created = Date(dateString: createdStr) ?? Date() - //let started = Date(dateString: startedStr) + let retries = dictionary["retries"] as? Int? ?? 0 { + + self.queue = queue + self.taskID = taskID + self.taskType = taskType + self.data = data + self.created = Date(dateString: createdStr) ?? Date() + self.retries = retries - let priority = Operation.QueuePriority(rawValue: queuePriority) ?? .normal - let qos = QualityOfService(rawValue: qualityOfService) ?? .utility + super.init() + self.qualityOfService = QualityOfService(rawValue: qualityOfService) ?? .utility - self.init(queue: queue, taskID: taskID, taskType: taskType, - dependencyStrs: dependencyStrs, data: data, created: created, - started: nil, 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: Any] { @@ -166,52 +146,28 @@ open class SYNQueueTask : Operation { } /** - Setup the dependencies for the task - - - parameter allTasks: Array of SYNQueueTasks that are dependencies of this task - */ - open 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 - */ + 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["taskID"] = self.taskID as Any dict["taskType"] = self.taskType as Any - dict["dependencies"] = self.dependencyStrs as Any dict["queuePriority"] = self.queuePriority.rawValue as Any dict["qualityOfService"] = self.qualityOfService.rawValue as Any dict["data"] = self.data dict["created"] = self.created.toISOString() - //dict["started"] = (self.started != nil) ? self.started!.toISOString() : nil - if let started = self.started { - dict["started"] = started.toISOString() - } dict["retries"] = 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 - */ + 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() @@ -232,8 +188,8 @@ open class SYNQueueTask : Operation { } /** - Starts executing the task - */ + Starts executing the task + */ open override func start() { super.start() @@ -242,8 +198,8 @@ open class SYNQueueTask : Operation { } /** - Cancels the task - */ + Cancels the task + */ open override func cancel() { lastError = NSError(domain: "SYNQueue", code: -1, userInfo: [NSLocalizedDescriptionKey: "Task \(taskID) was cancelled"]) @@ -261,11 +217,11 @@ open class SYNQueueTask : Operation { } /** - 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 - */ + 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 diff --git a/SYNQueue/SYNQueueTests/SYNQueueTests.swift b/SYNQueue/SYNQueueTests/SYNQueueTests.swift index b0da733..ec93dfb 100755 --- a/SYNQueue/SYNQueueTests/SYNQueueTests.swift +++ b/SYNQueue/SYNQueueTests/SYNQueueTests.swift @@ -50,7 +50,7 @@ class SYNQueueTests: XCTestCase { } queue.addTaskHandler(testTaskType) { $0.completed(nil) } - let task = SYNQueueTask(queue: queue, taskType: testTaskType, data: nil) + let task = SYNQueueTask(queue: queue, taskType: testTaskType) queue.addOperation(task) XCTAssert(queue.operationCount == 1) @@ -73,7 +73,7 @@ class SYNQueueTests: XCTestCase { Thread.sleep(forTimeInterval: 2) $0.completed(nil) } - let task = SYNQueueTask(queue: queue!, taskType: testTaskType, data: nil) + let task = SYNQueueTask(queue: queue!, taskType: testTaskType) queue!.addOperation(task) // Nil out the queue to simulate app backgrounded or quit @@ -101,7 +101,7 @@ class SYNQueueTests: XCTestCase { Thread.sleep(forTimeInterval: 1) $0.completed(nil) } - let task = SYNQueueTask(queue: queue, taskType: testTaskType, data: nil) + let task = SYNQueueTask(queue: queue, taskType: testTaskType) self.measure() { queue.addOperation(task) diff --git a/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift b/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift index a1ae7bb..dfa315d 100755 --- a/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift +++ b/SYNQueueDemo/SYNQueueDemo/UserDefaultsSerializer.swift @@ -11,33 +11,29 @@ class UserDefaultsSerializer : SYNQueueSerializationProvider { func serializeTask(_ task: SYNQueueTask, queueName: String) { if let serialized = task.toJSONString() { - let defaults = UserDefaults.standard var stringArray: [String] - - if let curStringArray = defaults.stringArray(forKey: 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)") } } func deserializeTasks(_ queue: SYNQueue) -> [SYNQueueTask] { - let defaults = UserDefaults.standard if let queueName = queue.name, - let stringArray = defaults.stringArray(forKey: 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 [] } @@ -50,8 +46,9 @@ class UserDefaultsSerializer : SYNQueueSerializationProvider { .map { return $0.toJSONString() } .filter { return $0 != nil } .map { return $0! } - - UserDefaults.standard.setValue(stringArray, forKey: queueName) + + UserDefaults.standard.set(stringArray, forKey: queueName) + UserDefaults.standard.synchronize() } } } diff --git a/SYNQueueDemo/SYNQueueDemo/ViewController.swift b/SYNQueueDemo/SYNQueueDemo/ViewController.swift index 42d8102..cceff89 100755 --- a/SYNQueueDemo/SYNQueueDemo/ViewController.swift +++ b/SYNQueueDemo/SYNQueueDemo/ViewController.swift @@ -13,7 +13,7 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection let reachability = Reachability.init() var totalTasksSeen = 0 - var nextTaskID = 1 + lazy var queue: SYNQueue = { return SYNQueue(queueName: "myQueue", maxConcurrency: 2, @@ -30,13 +30,6 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection queue.addTaskHandler("cellTask", taskHandler: taskHandler) queue.loadSerializedTasks() - - print("pre-loaded tasks: \(UserDefaults.standard.stringArray(forKey: "myQueue") ?? [])") - - let taskIDs = queue.operations - .map { return $0 as! SYNQueueTask } - .map { return Int($0.taskID) ?? 0 } - nextTaskID = (arrayMax(taskIDs) ?? 0) + 1 } override func viewDidLayoutSubviews() { @@ -81,7 +74,6 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection } if queue.operationCount == 0 { - nextTaskID = 1 totalTasksSeen = 0 } @@ -124,18 +116,11 @@ class ViewController: UIViewController, UICollectionViewDataSource, UICollection // MARK: - IBActions @IBAction func addTapped(_ sender: UIButton) { - let taskID1 = nextTaskID - nextTaskID += 1 - let task1 = SYNQueueTask(queue: queue, taskID: String(describing: taskID1), - taskType: "cellTask", dependencyStrs: [], data: [:]) - let shouldAddDependency = UserDefaults.standard.bool(forKey: kAddDependencySettingKey) - if shouldAddDependency { - let taskID2 = nextTaskID - nextTaskID += 1 - let task2 = SYNQueueTask(queue: queue, taskID: String(describing: 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) From 71fac06ede218a92e82dd9994e9e357ca24439e1 Mon Sep 17 00:00:00 2001 From: Rajavelu Chandrasekaran Date: Thu, 8 Jun 2017 18:50:44 +0800 Subject: [PATCH 6/6] Replaced hardcoded key strings in serialization with let contstants. --- SYNQueue/SYNQueue/SYNQueueTask.swift | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/SYNQueue/SYNQueue/SYNQueueTask.swift b/SYNQueue/SYNQueue/SYNQueueTask.swift index f11bc64..03039b6 100755 --- a/SYNQueue/SYNQueue/SYNQueueTask.swift +++ b/SYNQueue/SYNQueue/SYNQueueTask.swift @@ -11,6 +11,13 @@ 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 */ @@ -103,12 +110,12 @@ open class SYNQueueTask : Operation { } public init?(dictionary: JSONDictionary, queue: SYNQueue) { - if let taskID = dictionary["taskID"] as? String, - let taskType = dictionary["taskType"] as? String, - let qualityOfService = dictionary["qualityOfService"] as? Int, - let data: Any? = dictionary["data"] as Any??, - let createdStr = dictionary["created"] as? String, - let retries = dictionary["retries"] as? Int? ?? 0 { + 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 @@ -152,13 +159,12 @@ open class SYNQueueTask : Operation { */ open func toDictionary() -> [String: Any?] { var dict = [String: Any?]() - dict["taskID"] = self.taskID as Any - dict["taskType"] = self.taskType as Any - dict["queuePriority"] = self.queuePriority.rawValue as Any - dict["qualityOfService"] = self.qualityOfService.rawValue as Any - dict["data"] = self.data - dict["created"] = self.created.toISOString() - dict["retries"] = self.retries as 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 }