diff --git a/CHANGELOG.md b/CHANGELOG.md index efe16c3..9df7fea 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Release 0.9.0 + +- [x] Removed deprecated DispatchQueue methods and initializers. + # Release 0.8.1 - [x] Deprecated some DispatchQueue methods and initializers. diff --git a/Kommander.podspec b/Kommander.podspec index 14eb552..e62d2e6 100755 --- a/Kommander.podspec +++ b/Kommander.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Kommander' - s.version = '0.8.1' + s.version = '0.9.0' s.summary = 'A command pattern implementation written in Swift 4' s.homepage = 'https://github.com/intelygenz/Kommander-iOS' diff --git a/Kommander.xcodeproj/project.pbxproj b/Kommander.xcodeproj/project.pbxproj index e7f434b..6bd9db7 100755 --- a/Kommander.xcodeproj/project.pbxproj +++ b/Kommander.xcodeproj/project.pbxproj @@ -979,8 +979,8 @@ CURRENT_PROJECT_VERSION = "$(DYLIB_CURRENT_VERSION)"; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 3VW789WSMP; - DYLIB_COMPATIBILITY_VERSION = 0.8.0; - DYLIB_CURRENT_VERSION = 0.8.1; + DYLIB_COMPATIBILITY_VERSION = 0.9.0; + DYLIB_CURRENT_VERSION = 0.9.0; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1051,8 +1051,8 @@ CURRENT_PROJECT_VERSION = "$(DYLIB_CURRENT_VERSION)"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 3VW789WSMP; - DYLIB_COMPATIBILITY_VERSION = 0.8.0; - DYLIB_CURRENT_VERSION = 0.8.1; + DYLIB_COMPATIBILITY_VERSION = 0.9.0; + DYLIB_CURRENT_VERSION = 0.9.0; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; diff --git a/KommanderTests/DispatcherTests.swift b/KommanderTests/DispatcherTests.swift index 1701912..c1d8d3d 100644 --- a/KommanderTests/DispatcherTests.swift +++ b/KommanderTests/DispatcherTests.swift @@ -15,7 +15,7 @@ class DispatcherTests: XCTestCase { override func setUp() { super.setUp() - dispatcher = Dispatcher() + dispatcher = .default } override func tearDown() { @@ -42,7 +42,8 @@ class DispatcherTests: XCTestCase { } func testDefaultDispatcherDispatchQueue() { - let dispatchWorkItem = dispatcher.execute(qos: nil, flags: nil) { sleep(2) } + let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .assignCurrentContext) { sleep(2) } + dispatcher.execute(dispatchWorkItem) XCTAssertFalse(dispatchWorkItem.isCancelled) dispatchWorkItem.cancel() XCTAssertTrue(dispatchWorkItem.isCancelled) @@ -63,22 +64,8 @@ class DispatcherTests: XCTestCase { } } - func testCustomDispatcherDispatchQueue() { - let randomName = UUID().uuidString - dispatcher = Dispatcher(label: randomName, qos: .background, attributes: nil, autoreleaseFrequency: nil, target: nil) - XCTAssertEqual(dispatcher.dispatchQueue.label, randomName) - XCTAssertEqual(dispatcher.dispatchQueue.qos, .background) - if let dispatchWorkItem = dispatcher.execute({ sleep(2) }) as? DispatchWorkItem { - XCTAssertFalse(dispatchWorkItem.isCancelled) - dispatchWorkItem.cancel() - XCTAssertTrue(dispatchWorkItem.isCancelled) - } else { - XCTFail("Custom dispatcher isn't using DispatchQueue.") - } - } - func testMainDispatcherOperationQueue() { - dispatcher = MainDispatcher() + dispatcher = .main if let operation = dispatcher.execute({ sleep(2) }) as? Operation { XCTAssertEqual(dispatcher.operationQueue, OperationQueue.main) XCTAssertGreaterThan(dispatcher.operationQueue.operationCount, 0) @@ -90,8 +77,9 @@ class DispatcherTests: XCTestCase { } func testMainDispatcherDispatchQueue() { - dispatcher = MainDispatcher() - let dispatchWorkItem = dispatcher.execute(qos: nil, flags: nil, block: { sleep(2) }) + dispatcher = .main + let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .assignCurrentContext) { sleep(2) } + dispatcher.execute(dispatchWorkItem) XCTAssertEqual(dispatcher.dispatchQueue, DispatchQueue.main) XCTAssertFalse(dispatchWorkItem.isCancelled) dispatchWorkItem.cancel() @@ -101,7 +89,7 @@ class DispatcherTests: XCTestCase { func testCurrentDispatcherOperationQueue() { let operationQueue = OperationQueue() operationQueue.addOperation { - self.dispatcher = CurrentDispatcher() + self.dispatcher = .current if let operation = self.dispatcher.execute({ sleep(2) }) as? Operation { XCTAssertGreaterThan(self.dispatcher.operationQueue.operationCount, 0) operation.cancel() @@ -115,8 +103,9 @@ class DispatcherTests: XCTestCase { func testCurrentDispatcherDispatchQueue() { let dispatchQueue = DispatchQueue(label: "") dispatchQueue.async { - self.dispatcher = CurrentDispatcher() - let dispatchWorkItem = self.dispatcher.execute(qos: nil, flags: nil, block: { sleep(2) }) + self.dispatcher = .current + let dispatchWorkItem = DispatchWorkItem(qos: .default, flags: .assignCurrentContext) { sleep(2) } + self.dispatcher.execute(dispatchWorkItem) XCTAssertFalse(dispatchWorkItem.isCancelled) dispatchWorkItem.cancel() XCTAssertTrue(dispatchWorkItem.isCancelled) diff --git a/KommanderTests/KommanderTests.swift b/KommanderTests/KommanderTests.swift index 3b23813..0505af9 100755 --- a/KommanderTests/KommanderTests.swift +++ b/KommanderTests/KommanderTests.swift @@ -74,6 +74,57 @@ class KommanderTests: XCTestCase { waitForExpectations(timeout: 100, handler: nil) } + func test_nCalls_withDelay() { + + let ex = expectation(description: String(describing: type(of: self))) + + var successes = 0 + let calls = Int(arc4random_uniform(10) + 1) + + for i in 0..=calls { + ex.fulfill() + } + }) + .onError({ (error) in + ex.fulfill() + XCTFail() + }) + .execute(after: .seconds(1)) + } + + waitForExpectations(timeout: 100, handler: nil) + } + + func test_nCalls_andCancel() { + + let ex = expectation(description: String(describing: type(of: self))) + + var errors = 0 + let calls = Int(arc4random_uniform(10) + 1) + + for i in 0..=calls { + ex.fulfill() + } + }) + .execute() + .cancel(true, after: .seconds(2)) + } + + waitForExpectations(timeout: 100, handler: nil) + } + func test_nCalls() { let ex = expectation(description: String(describing: type(of: self))) @@ -211,6 +262,19 @@ class KommanderTests: XCTestCase { waitForExpectations(timeout: 100, handler: nil) } + func testInitializers() { + let custom = Kommander(name: "Test", maxConcurrentOperationCount: 2) + XCTAssertEqual(custom.executor.operationQueue.name, "Test") + XCTAssertEqual(custom.executor.operationQueue.maxConcurrentOperationCount, 2) + XCTAssertEqual(Kommander.main.executor.operationQueue, OperationQueue.main) + XCTAssertEqual(Kommander.current.executor.operationQueue, OperationQueue.current) + XCTAssertEqual(Kommander.default.executor.operationQueue.qualityOfService, .default) + XCTAssertEqual(Kommander.userInteractive.executor.operationQueue.qualityOfService, .userInteractive) + XCTAssertEqual(Kommander.userInitiated.executor.operationQueue.qualityOfService, .userInitiated) + XCTAssertEqual(Kommander.utility.executor.operationQueue.qualityOfService, .utility) + XCTAssertEqual(Kommander.background.executor.operationQueue.qualityOfService, .background) + } + } extension KommanderTests { diff --git a/README.md b/README.md index 82c5d12..fc06a91 100755 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ dependencies: [ ## Usage +#### Making, executing and cancelling Kommands: + ```swift Kommander().makeKommand { // Your code here @@ -86,7 +88,13 @@ Kommander().makeKommand { ``` ```swift -Kommander().makeKommand { () -> String in +Kommander().makeKommand { + // Your code here +}.execute(after: .seconds(2)) +``` + +```swift +Kommander().makeKommand { return "Your string" }.onSuccess { yourString in print(yourString) @@ -113,6 +121,56 @@ let kommand = Kommander().makeKommand { () -> Any? in kommand.cancel() ``` +#### Creating Kommanders: + +```swift +Kommander(deliverer: Dispatcher = .current, executor: Dispatcher = .default) + +Kommander(deliverer: Dispatcher = .current, name: String, qos: QualityOfService = .default, maxConcurrentOperationCount: Int = .default) +``` + +```swift +Kommander.main + +Kommander.current + +Kommander.default + +Kommander.userInteractive + +Kommander.userInitiated + +Kommander.utility + +Kommander.background +``` + +#### Creating Dispatchers: + +```swift +CurrentDispatcher() + +MainDispatcher() + +Dispatcher(name: String, qos: QualityOfService = .default, maxConcurrentOperationCount: Int = .default) +``` + +```swift +Dispatcher.main + +Dispatcher.current + +Dispatcher.default + +Dispatcher.userInteractive + +Dispatcher.userInitiated + +Dispatcher.utility + +Dispatcher.background +``` + ## Etc. * Contributions are very welcome. diff --git a/Source/Dispatcher.swift b/Source/Dispatcher.swift index 50e4a4f..c721abc 100755 --- a/Source/Dispatcher.swift +++ b/Source/Dispatcher.swift @@ -23,30 +23,20 @@ open class Dispatcher { /// Dispatcher with default quality of service open static var `default`: Dispatcher { return Dispatcher() } /// Dispatcher with user interactive quality of service - open static var userInteractive: Dispatcher { return Dispatcher(name: nil, qos: .userInteractive) } + open static var userInteractive: Dispatcher { return Dispatcher(qos: .userInteractive) } /// Dispatcher with user initiated quality of service - open static var userInitiated: Dispatcher { return Dispatcher(name: nil, qos: .userInitiated) } + open static var userInitiated: Dispatcher { return Dispatcher(qos: .userInitiated) } /// Dispatcher with utility quality of service - open static var utility: Dispatcher { return Dispatcher(name: nil, qos: .utility) } + open static var utility: Dispatcher { return Dispatcher(qos: .utility) } /// Dispatcher with background quality of service - open static var background: Dispatcher { return Dispatcher(name: nil, qos: .background) } - - /// Dispatcher instance with default OperationQueue - public convenience init() { - self.init(name: nil, qos: .default) - } + open static var background: Dispatcher { return Dispatcher(qos: .background) } /// Dispatcher instance with custom OperationQueue - public init(name: String?, qos: QualityOfService?, maxConcurrentOperationCount: Int? = nil) { - operationQueue.name = name ?? UUID().uuidString - operationQueue.qualityOfService = qos ?? .default - operationQueue.maxConcurrentOperationCount = maxConcurrentOperationCount ?? OperationQueue.defaultMaxConcurrentOperationCount - } - - /// Dispatcher instance with custom DispatchQueue - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `Dispatcher.init(name:qos:maxConcurrentOperationCount:)` instead.") - public init(label: String?, qos: DispatchQoS?, attributes: DispatchQueue.Attributes? = nil, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency? = nil, target: DispatchQueue? = nil) { - dispatchQueue = DispatchQueue(label: label ?? UUID().uuidString, qos: qos ?? .default, attributes: attributes ?? .concurrent, autoreleaseFrequency: autoreleaseFrequency ?? .inherit, target: target) + public init(name: String = UUID().uuidString, qos: QualityOfService = .default, maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount) { + operationQueue.name = name + operationQueue.qualityOfService = qos + operationQueue.maxConcurrentOperationCount = maxConcurrentOperationCount + dispatchQueue = DispatchQueue(label: name, qos: dispatchQoS(qos), attributes: .concurrent, autoreleaseFrequency: .inherit, target: operationQueue.underlyingQueue) } /// Execute Operation instance in OperationQueue @@ -89,15 +79,6 @@ open class Dispatcher { dispatchQueue.asyncAfter(deadline: .now() + delay, execute: block) } - /// Execute block in DispatchQueue using custom DispatchWorkItem instance after delay - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `execute(delay:work:)` instead.") - open func execute(after delay: DispatchTimeInterval, qos: DispatchQoS?, flags: DispatchWorkItemFlags?, block: @escaping @convention(block) () -> ()) { - guard delay != .never else { - return - } - dispatchQueue.asyncAfter(deadline: .now() + delay, qos: qos ?? .default, flags: flags ?? .assignCurrentContext, execute: block) - } - /// Execute DispatchWorkItem instance in DispatchQueue after delay open func execute(after delay: DispatchTimeInterval, work: DispatchWorkItem) { guard delay != .never else { @@ -107,17 +88,19 @@ open class Dispatcher { dispatchQueue.asyncAfter(deadline: .now() + delay, execute: work) } - /// Execute block in DispatchQueue using custom DispatchWorkItem instance - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `execute(work:)` instead.") - @discardableResult open func execute(qos: DispatchQoS?, flags: DispatchWorkItemFlags?, block: @escaping @convention(block) () -> ()) -> DispatchWorkItem { - let work = DispatchWorkItem(qos: qos ?? .default, flags: flags ?? .assignCurrentContext, block: block) - execute(work) - return work - } - /// Execute DispatchWorkItem instance in DispatchQueue open func execute(_ work: DispatchWorkItem) { dispatchQueue.async(execute: work) } + private final func dispatchQoS(_ qos: QualityOfService) -> DispatchQoS { + switch qos { + case .userInteractive: return .userInteractive + case .userInitiated: return .userInitiated + case .utility: return .utility + case .background: return .background + default: return .default + } + } + } diff --git a/Source/Kommand.swift b/Source/Kommand.swift index 48c455f..736ec18 100644 --- a/Source/Kommand.swift +++ b/Source/Kommand.swift @@ -51,7 +51,7 @@ open class Kommand { final weak var work: DispatchWorkItem? /// Kommand instance with your deliverer, your executor and your actionBlock returning generic and throwing errors - public init(deliverer: Dispatcher, executor: Dispatcher, actionBlock: @escaping ActionBlock) { + public init(deliverer: Dispatcher = .current, executor: Dispatcher = .default, actionBlock: @escaping ActionBlock) { self.deliverer = deliverer self.executor = executor self.actionBlock = actionBlock diff --git a/Source/Kommander.swift b/Source/Kommander.swift index ccdcba6..4252cb4 100644 --- a/Source/Kommander.swift +++ b/Source/Kommander.swift @@ -12,71 +12,37 @@ import Foundation open class Kommander { /// Deliverer - private final let deliverer: Dispatcher + final let deliverer: Dispatcher /// Executor - private final let executor: Dispatcher + final let executor: Dispatcher /// Kommander instance with CurrentDispatcher deliverer and MainDispatcher executor - open static var main: Kommander { return Kommander(executor: Dispatcher.main) } + open static var main: Kommander { return Kommander(executor: .main) } /// Kommander instance with CurrentDispatcher deliverer and CurrentDispatcher executor - open static var current: Kommander { return Kommander(executor: Dispatcher.current) } + open static var current: Kommander { return Kommander(executor: .current) } /// Kommander instance with CurrentDispatcher deliverer and Dispatcher executor with default quality of service - open static var `default`: Kommander { return Kommander(executor: Dispatcher.default) } + open static var `default`: Kommander { return Kommander() } /// Kommander instance with CurrentDispatcher deliverer and Dispatcher executor with user interactive quality of service - open static var userInteractive: Kommander { return Kommander(executor: Dispatcher.userInteractive) } + open static var userInteractive: Kommander { return Kommander(executor: .userInteractive) } /// Kommander instance with CurrentDispatcher deliverer and Dispatcher executor with user initiated quality of service - open static var userInitiated: Kommander { return Kommander(executor: Dispatcher.userInitiated) } + open static var userInitiated: Kommander { return Kommander(executor: .userInitiated) } /// Kommander instance with CurrentDispatcher deliverer and Dispatcher executor with utility quality of service - open static var utility: Kommander { return Kommander(executor: Dispatcher.utility) } + open static var utility: Kommander { return Kommander(executor: .utility) } /// Kommander instance with CurrentDispatcher deliverer and Dispatcher executor with background quality of service - open static var background: Kommander { return Kommander(executor: Dispatcher.background) } + open static var background: Kommander { return Kommander(executor: .background) } - /// Kommander instance with CurrentDispatcher deliverer and default Dispatcher executor - public convenience init() { - self.init(deliverer: nil, executor: nil) + /// Kommander instance with deliverer and executor + public init(deliverer: Dispatcher = .current, executor: Dispatcher = .default) { + self.deliverer = deliverer + self.executor = executor } - /// Kommander instance with CurrentDispatcher deliverer and your executor - public convenience init(executor: Dispatcher) { - self.init(deliverer: nil, executor: executor) - } - - /// Kommander instance with your deliverer and default Dispatcher executor - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `Kommander.init(deliverer:executor:)` instead.") - public convenience init(deliverer: Dispatcher) { - self.init(deliverer: deliverer, executor: nil) - } - - /// Kommander instance with your deliverer and your executor - public init(deliverer: Dispatcher?, executor: Dispatcher?) { - self.deliverer = deliverer ?? CurrentDispatcher() - self.executor = executor ?? Dispatcher() - } - - /// Kommander instance with CurrentDispatcher deliverer and custom OperationQueue executor - public convenience init(name: String?, qos: QualityOfService?, maxConcurrentOperationCount: Int) { - self.init(deliverer: nil, name: name, qos: qos, maxConcurrentOperationCount: maxConcurrentOperationCount) - } - - /// Kommander instance with CurrentDispatcher deliverer and custom DispatchQueue executor - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `Kommander.init(name:qos:maxConcurrentOperationCount:)` instead.") - public convenience init(name: String?, qos: DispatchQoS?, attributes: DispatchQueue.Attributes?, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency?, target: DispatchQueue?) { - self.init(deliverer: nil, name: name, qos: qos, attributes: attributes, autoreleaseFrequency: autoreleaseFrequency, target: target) - } - - /// Kommander instance with your deliverer and custom OperationQueue executor - public init(deliverer: Dispatcher?, name: String?, qos: QualityOfService?, maxConcurrentOperationCount: Int) { - self.deliverer = deliverer ?? CurrentDispatcher() + /// Kommander instance with deliverer and custom OperationQueue executor + public init(deliverer: Dispatcher = .current, name: String = UUID().uuidString, qos: QualityOfService = .default, maxConcurrentOperationCount: Int = OperationQueue.defaultMaxConcurrentOperationCount) { + self.deliverer = deliverer executor = Dispatcher(name: name, qos: qos, maxConcurrentOperationCount: maxConcurrentOperationCount) } - /// Kommander instance with your deliverer and custom DispatchQueue executor - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `Kommander.init(deliverer:name:qos:maxConcurrentOperationCount:)` instead.") - public init(deliverer: Dispatcher?, name: String?, qos: DispatchQoS?, attributes: DispatchQueue.Attributes?, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency?, target: DispatchQueue?) { - self.deliverer = deliverer ?? CurrentDispatcher() - executor = Dispatcher(label: name, qos: qos, attributes: attributes, autoreleaseFrequency: autoreleaseFrequency, target: target) - } - /// Build Kommand instance with an actionBlock returning generic and throwing errors open func makeKommand(_ actionBlock: @escaping () throws -> Result) -> Kommand { return Kommand(deliverer: deliverer, executor: executor, actionBlock: actionBlock) diff --git a/Source/MainDispatcher.swift b/Source/MainDispatcher.swift index d79c495..83eda77 100755 --- a/Source/MainDispatcher.swift +++ b/Source/MainDispatcher.swift @@ -13,22 +13,9 @@ open class MainDispatcher: Dispatcher { /// Dispatcher instance with main OperationQueue public init() { - super.init(name: nil, qos: nil) + super.init() operationQueue = OperationQueue.main dispatchQueue = DispatchQueue.main } - /// - Warning: You can't use this initializer! - private override convenience init(name: String?, qos: QualityOfService?, maxConcurrentOperationCount: Int? = nil) { - self.init() - assertionFailure("You can't use this initializer for a \(String(describing: type(of: self))).") - } - - /// - Warning: You can't use this initializer! - @available(*, deprecated, message: "This will be removed in Kommander 0.9. Use `MainDispatcher.init()` instead.") - private override convenience init(label: String?, qos: DispatchQoS?, attributes: DispatchQueue.Attributes?, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency?, target: DispatchQueue?) { - self.init() - assertionFailure("You can't use this initializer for a \(String(describing: type(of: self))).") - } - }