From dc1b5d7d521493831ecb0d4bb33fb32f40a6d176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Rupe=CC=81rez?= Date: Tue, 31 Jan 2017 18:19:04 +0100 Subject: [PATCH] Allowing make, execute and cancel multiple Kommands concurrently, sequentially and waiting until finished. --- Kommander/Dispatcher.swift | 22 ++++++ Kommander/Kommand.swift | 8 +- Kommander/Kommander.swift | 35 +++++++++ KommanderTests/KommanderTests.swift | 112 ++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 4 deletions(-) diff --git a/Kommander/Dispatcher.swift b/Kommander/Dispatcher.swift index 21f6812..f341719 100644 --- a/Kommander/Dispatcher.swift +++ b/Kommander/Dispatcher.swift @@ -52,6 +52,28 @@ public class Dispatcher { } } + public func execute(_ blocks: [() -> Void], concurrent: Bool = true, waitUntilFinished: Bool = false) -> [Any] { + var actions = [Any]() + if concurrent { + for block in blocks { + actions.append(execute(block)) + } + } + else { + for block in blocks { + let blockOperation = BlockOperation(block: block) + if let lastOperation = actions.last as? Operation { + blockOperation.addDependency(lastOperation) + } + actions.append(blockOperation) + } + if let operations = actions as? [Operation] { + execute(operations, waitUntilFinished: waitUntilFinished) + } + } + return actions + } + public func execute(qos: DispatchQoS?, flags: DispatchWorkItemFlags?, block: @escaping @convention(block) () -> ()) -> DispatchWorkItem { let work = DispatchWorkItem(qos: qos ?? .default, flags: flags ?? .assignCurrentContext, block: block) execute(work) diff --git a/Kommander/Kommand.swift b/Kommander/Kommand.swift index a150bb7..7b83a10 100644 --- a/Kommander/Kommand.swift +++ b/Kommander/Kommand.swift @@ -16,10 +16,10 @@ public class Kommand { private final let deliverer: Dispatcher private final let executor: Dispatcher - private final let actionBlock: ActionBlock - private final var successBlock: SuccessBlock? - private final var errorBlock: ErrorBlock? - private final var action: Any? + internal final let actionBlock: ActionBlock + private(set) internal final var successBlock: SuccessBlock? + private(set) internal final var errorBlock: ErrorBlock? + internal final var action: Any? internal init(deliverer: Dispatcher, executor: Dispatcher, actionBlock: @escaping ActionBlock) { self.deliverer = deliverer diff --git a/Kommander/Kommander.swift b/Kommander/Kommander.swift index 49f1d0e..d700ec7 100644 --- a/Kommander/Kommander.swift +++ b/Kommander/Kommander.swift @@ -52,4 +52,39 @@ public class Kommander { return Kommand(deliverer: deliverer, executor: executor, actionBlock: actionBlock) } + public func makeKommands(_ actionBlocks: [() throws -> T]) -> [Kommand] { + var kommands = [Kommand]() + for actionBlock in actionBlocks { + kommands.append(Kommand(deliverer: deliverer, executor: executor, actionBlock: actionBlock)) + } + return kommands + } + + func execute(_ kommands: [Kommand], concurrent: Bool = true, waitUntilFinished: Bool = false) { + let blocks = kommands.map { kommand -> () -> Void in + return { + do { + let result = try kommand.actionBlock() + _ = self.deliverer.execute { + kommand.successBlock?(result) + } + } catch { + _ = self.deliverer.execute { + kommand.errorBlock?(error) + } + } + } + } + let actions = executor.execute(blocks, concurrent: concurrent, waitUntilFinished: waitUntilFinished) + for (index, kommand) in kommands.enumerated() { + kommand.action = actions[index] + } + } + + func cancel(_ kommands: [Kommand]) { + for kommand in kommands { + kommand.cancel() + } + } + } diff --git a/KommanderTests/KommanderTests.swift b/KommanderTests/KommanderTests.swift index 40ca817..fe78510 100644 --- a/KommanderTests/KommanderTests.swift +++ b/KommanderTests/KommanderTests.swift @@ -99,6 +99,118 @@ class KommanderTests: XCTestCase { waitForExpectations(timeout: 100, handler: nil) } + func test_nCalls_concurrent_waitUntilFinished() { + + let ex = expectation(description: String(describing: type(of: self))) + + var successes = 0 + let calls = Int(arc4random_uniform(10) + 1) + + var kommands = [Kommand]() + + for i in 0..=calls { + ex.fulfill() + } + }) + .onError({ (error) in + ex.fulfill() + XCTFail() + })) + } + + interactor.kommander.execute(kommands, concurrent: true, waitUntilFinished: true) + + waitForExpectations(timeout: 100, handler: nil) + } + + func test_nCalls_concurrent() { + + let ex = expectation(description: String(describing: type(of: self))) + + var successes = 0 + let calls = Int(arc4random_uniform(10) + 1) + + var kommands = [Kommand]() + + for i in 0..=calls { + ex.fulfill() + } + }) + .onError({ (error) in + ex.fulfill() + XCTFail() + })) + } + + interactor.kommander.execute(kommands, concurrent: true, waitUntilFinished: false) + + waitForExpectations(timeout: 100, handler: nil) + } + + func test_nCalls_sequential_waitUntilFinished() { + + let ex = expectation(description: String(describing: type(of: self))) + + var successes = 0 + let calls = Int(arc4random_uniform(10) + 1) + + var kommands = [Kommand]() + + for i in 0..=calls { + ex.fulfill() + } + }) + .onError({ (error) in + ex.fulfill() + XCTFail() + })) + } + + interactor.kommander.execute(kommands, concurrent: false, waitUntilFinished: true) + + waitForExpectations(timeout: 100, handler: nil) + } + + func test_nCalls_sequential() { + + let ex = expectation(description: String(describing: type(of: self))) + + var successes = 0 + let calls = Int(arc4random_uniform(10) + 1) + + var kommands = [Kommand]() + + for i in 0..=calls { + ex.fulfill() + } + }) + .onError({ (error) in + ex.fulfill() + XCTFail() + })) + } + + interactor.kommander.execute(kommands, concurrent: false, waitUntilFinished: false) + + waitForExpectations(timeout: 100, handler: nil) + } + } extension KommanderTests {