Skip to content

Commit 4bdab82

Browse files
committed
Add convert() & zip()
1 parent c4212c0 commit 4bdab82

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

SwiftTask/SwiftTask.swift

+66
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,74 @@ internal func _bindInnerTask<Progress2, Value2, Error, Error2>(
631631
}
632632
}
633633

634+
private func _toAny<A>(a: A) -> Any
635+
{
636+
return a
637+
}
638+
639+
extension Task
640+
{
641+
/// converts Task<P, V, E> to Task<V2, V2, E2>
642+
public func convert<Progress2, Value2, Error2>(progress toP2: Progress -> Progress2, value toV2: Value -> Value2, error toE2: Error -> Error2) -> Task<Progress2, Value2, Error2>
643+
{
644+
return Task<Progress2, Value2, Error2> { machine, progress, fulfill, _reject, configure in
645+
configure.pause = { self.pause() }
646+
configure.resume = { self.resume() }
647+
configure.cancel = { self.cancel() }
648+
649+
self.progress { _, p in
650+
progress(toP2(p))
651+
}.then { value, errorInfo -> Void in
652+
if let value = value {
653+
fulfill(toV2(value))
654+
}
655+
else if let errorInfo = errorInfo {
656+
let (error, isCancelled) = errorInfo
657+
_reject((error.map(toE2), isCancelled))
658+
}
659+
}
660+
}
661+
}
662+
}
663+
634664
// MARK: - Multiple Tasks
635665

666+
extension Task
667+
{
668+
/// combines 2 tasks into one which fulfills when both tasks are fulfilled
669+
public func zip<Progress2, Value2, Error2>(task2: Task<Progress2, Value2, Error2>) -> Task<(Progress?, Progress2?), (Value, Value2), (Error?, Error2?)>
670+
{
671+
return Task<(Progress?, Progress2?), (Value, Value2), (Error?, Error2?)> { machine, progress, fulfill, _reject, configure in
672+
673+
let t1 = self.convert(progress: _toAny, value: _toAny, error: _toAny)
674+
let t2 = task2.convert(progress: _toAny, value: _toAny, error: _toAny)
675+
let zipTask = Task<Any, Any, Any>.all([t1, t2])
676+
677+
configure.pause = { zipTask.pause() }
678+
configure.resume = { zipTask.resume() }
679+
configure.cancel = { zipTask.cancel() }
680+
681+
t1.progress { _, p in
682+
if t2.value == nil && t2.errorInfo == nil {
683+
progress((p as? Progress, t2.progress as? Progress2))
684+
}
685+
}
686+
t2.progress { _, p in
687+
if t1.value == nil && t1.errorInfo == nil {
688+
progress((t1.progress as? Progress, p as? Progress2))
689+
}
690+
}
691+
692+
zipTask.success { (valueTuple: [Any]) -> Void in
693+
fulfill((valueTuple[0] as! Value, valueTuple[1] as! Value2))
694+
}.failure { _, isCancelled -> Void in
695+
_reject((error: (t1.errorInfo?.error as? Error, t2.errorInfo?.error as? Error2), isCancelled: isCancelled))
696+
}
697+
698+
}.name("\(self.name)-zip(\(task2.name))")
699+
}
700+
}
701+
636702
extension Task
637703
{
638704
public typealias BulkProgress = (completedCount: Int, totalCount: Int)

SwiftTaskTests/SwiftTaskTests.swift

+77
Original file line numberDiff line numberDiff line change
@@ -1514,4 +1514,81 @@ class SwiftTaskTests: _TestCase
15141514

15151515
self.wait()
15161516
}
1517+
1518+
//--------------------------------------------------
1519+
// MARK: - Zip
1520+
//--------------------------------------------------
1521+
1522+
/// zip fulfilled test
1523+
func testZip_success()
1524+
{
1525+
// NOTE: this is async test
1526+
if !self.isAsync { return }
1527+
1528+
let expect = self.expectationWithDescription(__FUNCTION__)
1529+
1530+
let task1 = _interruptableTask(progressCount: 5, finalState: .Fulfilled)
1531+
let task2 = _interruptableTask(progressCount: 5, finalState: .Fulfilled)
1532+
.convert(progress: { "\($0)"}, value: { ($0, $0.characters.count as Int) }, error: { ($0, $0.characters.count as Int) }) // change Task type using `convert()`
1533+
1534+
let zipTask = task1.zip(task2)
1535+
1536+
zipTask.progress { (oldProgress, newProgress) in
1537+
1538+
print("newProgress = \(newProgress)")
1539+
1540+
}.then { value, errorInfo -> Void in
1541+
1542+
print("success = \(value)")
1543+
1544+
XCTAssertTrue(errorInfo == nil, "`zipTask` always fulfills.")
1545+
1546+
XCTAssertEqual(value!.0, "OK")
1547+
XCTAssertEqual(value!.1.0, "OK")
1548+
XCTAssertEqual(value!.1.1, 2)
1549+
// XCTAssertEqual(value, ("OK", ("OK", 2))) // doesn't work in Xcode7-beta5
1550+
1551+
expect.fulfill()
1552+
1553+
}
1554+
1555+
self.wait()
1556+
}
1557+
1558+
/// zip fulfilled test
1559+
func testZip_failure()
1560+
{
1561+
// NOTE: this is async test
1562+
if !self.isAsync { return }
1563+
1564+
let expect = self.expectationWithDescription(__FUNCTION__)
1565+
1566+
let task1 = _interruptableTask(progressCount: 5, finalState: .Rejected)
1567+
let task2 = _interruptableTask(progressCount: 5, finalState: .Rejected)
1568+
.convert(progress: { "\($0)"}, value: { ($0, $0.characters.count as Int) }, error: { ($0, $0.characters.count as Int) }) // change Task type using `convert()`
1569+
1570+
let zipTask = task2.zip(task1)
1571+
1572+
zipTask.progress { (oldProgress, newProgress) in
1573+
1574+
print("newProgress = \(newProgress)")
1575+
1576+
}.then { value, errorInfo -> Void in
1577+
1578+
print("errorInfo = \(errorInfo)")
1579+
1580+
XCTAssertTrue(value == nil, "`zipTask` always rejects.")
1581+
1582+
let error1 = errorInfo!.error!.0
1583+
let error2 = errorInfo!.error!.1
1584+
1585+
XCTAssertFalse(error1 == nil && error2 == nil, "`errorInfo!.error` should only have 1 inner error.")
1586+
XCTAssertFalse(error1 != nil && error2 != nil, "`errorInfo!.error` should only have 1 inner error.")
1587+
1588+
expect.fulfill()
1589+
1590+
}
1591+
1592+
self.wait()
1593+
}
15171594
}

0 commit comments

Comments
 (0)