diff --git a/SwiftTask/SwiftTask.swift b/SwiftTask/SwiftTask.swift index a4dadf2..4940361 100644 --- a/SwiftTask/SwiftTask.swift +++ b/SwiftTask/SwiftTask.swift @@ -631,8 +631,74 @@ internal func _bindInnerTask( } } +private func _toAny(a: A) -> Any +{ + return a +} + +extension Task +{ + /// converts Task to Task + public func convert(progress toP2: Progress -> Progress2, value toV2: Value -> Value2, error toE2: Error -> Error2) -> Task + { + return Task { machine, progress, fulfill, _reject, configure in + configure.pause = { self.pause() } + configure.resume = { self.resume() } + configure.cancel = { self.cancel() } + + self.progress { _, p in + progress(toP2(p)) + }.then { value, errorInfo -> Void in + if let value = value { + fulfill(toV2(value)) + } + else if let errorInfo = errorInfo { + let (error, isCancelled) = errorInfo + _reject((error.map(toE2), isCancelled)) + } + } + } + } +} + // MARK: - Multiple Tasks +extension Task +{ + /// combines 2 tasks into one which fulfills when both tasks are fulfilled + public func zip(task2: Task) -> Task<(Progress?, Progress2?), (Value, Value2), (Error?, Error2?)> + { + return Task<(Progress?, Progress2?), (Value, Value2), (Error?, Error2?)> { machine, progress, fulfill, _reject, configure in + + let t1 = self.convert(progress: _toAny, value: _toAny, error: _toAny) + let t2 = task2.convert(progress: _toAny, value: _toAny, error: _toAny) + let zipTask = Task.all([t1, t2]) + + configure.pause = { zipTask.pause() } + configure.resume = { zipTask.resume() } + configure.cancel = { zipTask.cancel() } + + t1.progress { _, p in + if t2.value == nil && t2.errorInfo == nil { + progress((p as? Progress, t2.progress as? Progress2)) + } + } + t2.progress { _, p in + if t1.value == nil && t1.errorInfo == nil { + progress((t1.progress as? Progress, p as? Progress2)) + } + } + + zipTask.success { (valueTuple: [Any]) -> Void in + fulfill((valueTuple[0] as! Value, valueTuple[1] as! Value2)) + }.failure { _, isCancelled -> Void in + _reject((error: (t1.errorInfo?.error as? Error, t2.errorInfo?.error as? Error2), isCancelled: isCancelled)) + } + + }.name("\(self.name)-zip(\(task2.name))") + } +} + extension Task { public typealias BulkProgress = (completedCount: Int, totalCount: Int) diff --git a/SwiftTaskTests/SwiftTaskTests.swift b/SwiftTaskTests/SwiftTaskTests.swift index f1ff310..47e6f93 100644 --- a/SwiftTaskTests/SwiftTaskTests.swift +++ b/SwiftTaskTests/SwiftTaskTests.swift @@ -1514,4 +1514,81 @@ class SwiftTaskTests: _TestCase self.wait() } + + //-------------------------------------------------- + // MARK: - Zip + //-------------------------------------------------- + + /// zip fulfilled test + func testZip_success() + { + // NOTE: this is async test + if !self.isAsync { return } + + let expect = self.expectationWithDescription(__FUNCTION__) + + let task1 = _interruptableTask(progressCount: 5, finalState: .Fulfilled) + let task2 = _interruptableTask(progressCount: 5, finalState: .Fulfilled) + .convert(progress: { "\($0)"}, value: { ($0, $0.characters.count as Int) }, error: { ($0, $0.characters.count as Int) }) // change Task type using `convert()` + + let zipTask = task1.zip(task2) + + zipTask.progress { (oldProgress, newProgress) in + + print("newProgress = \(newProgress)") + + }.then { value, errorInfo -> Void in + + print("success = \(value)") + + XCTAssertTrue(errorInfo == nil, "`zipTask` always fulfills.") + + XCTAssertEqual(value!.0, "OK") + XCTAssertEqual(value!.1.0, "OK") + XCTAssertEqual(value!.1.1, 2) +// XCTAssertEqual(value, ("OK", ("OK", 2))) // doesn't work in Xcode7-beta5 + + expect.fulfill() + + } + + self.wait() + } + + /// zip fulfilled test + func testZip_failure() + { + // NOTE: this is async test + if !self.isAsync { return } + + let expect = self.expectationWithDescription(__FUNCTION__) + + let task1 = _interruptableTask(progressCount: 5, finalState: .Rejected) + let task2 = _interruptableTask(progressCount: 5, finalState: .Rejected) + .convert(progress: { "\($0)"}, value: { ($0, $0.characters.count as Int) }, error: { ($0, $0.characters.count as Int) }) // change Task type using `convert()` + + let zipTask = task2.zip(task1) + + zipTask.progress { (oldProgress, newProgress) in + + print("newProgress = \(newProgress)") + + }.then { value, errorInfo -> Void in + + print("errorInfo = \(errorInfo)") + + XCTAssertTrue(value == nil, "`zipTask` always rejects.") + + let error1 = errorInfo!.error!.0 + let error2 = errorInfo!.error!.1 + + XCTAssertFalse(error1 == nil && error2 == nil, "`errorInfo!.error` should only have 1 inner error.") + XCTAssertFalse(error1 != nil && error2 != nil, "`errorInfo!.error` should only have 1 inner error.") + + expect.fulfill() + + } + + self.wait() + } }