Skip to content

Commit 0a19c3e

Browse files
committed
'Cancel' for PromiseKit -- provides the ability to cancel promises and promise chains
1 parent 1716ee6 commit 0a19c3e

6 files changed

+222
-49
lines changed

.travis.yml

+8
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ matrix:
1414
- {osx_image: xcode9.4, env: 'PLAT=iOS SWFT=3.3 DST="OS=11.4,name=iPhone 5s"'}
1515
- {osx_image: xcode9.4, env: 'PLAT=tvOS SWFT=3.3 DST="OS=11.4,name=Apple TV"'}
1616

17+
- {osx_image: xcode10, env: 'PLAT=macOS SWFT=3.4 DST="arch=x86_64"'}
18+
- {osx_image: xcode10, env: 'PLAT=iOS SWFT=3.4 DST="OS=12.0,name=iPhone SE"'}
19+
- {osx_image: xcode10, env: 'PLAT=tvOS SWFT=3.4 DST="OS=12.0,name=Apple TV"'}
20+
1721
- {osx_image: xcode9.2, env: 'PLAT=macOS SWFT=4.0 DST="arch=x86_64"'}
1822
- {osx_image: xcode9.2, env: 'PLAT=iOS SWFT=4.0 DST="OS=11.2,name=iPhone SE"'}
1923
- {osx_image: xcode9.2, env: 'PLAT=tvOS SWFT=4.0 DST="OS=11.2,name=Apple TV"'}
@@ -25,6 +29,10 @@ matrix:
2529
- {osx_image: xcode9.4, env: 'PLAT=iOS SWFT=4.1 DST="OS=11.4,name=iPhone 5s" TEST=1'}
2630
- {osx_image: xcode9.3, env: 'PLAT=tvOS SWFT=4.1 DST="OS=10.2,name=Apple TV 1080p"'}
2731
- {osx_image: xcode9.4, env: 'PLAT=tvOS SWFT=4.1 DST="OS=11.4,name=Apple TV" TEST=1'}
32+
33+
- {osx_image: xcode10, env: 'PLAT=macOS SWFT=4.2 DST="arch=x86_64"'}
34+
- {osx_image: xcode10, env: 'PLAT=iOS SWFT=4.2 DST="OS=12.0,name=iPhone SE"'}
35+
- {osx_image: xcode10, env: 'PLAT=tvOS SWFT=4.2 DST="OS=12.0,name=Apple TV"'}
2836
cache:
2937
directories:
3038
- Carthage

Cartfile

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
github "mxcl/PromiseKit" ~> 6.0
1+
#github "mxcl/PromiseKit" ~> 6.0
2+
github "dougzilla32/PromiseKit" "CoreCancel"

Cartfile.resolved

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
github "mxcl/PromiseKit" "6.3.3"
1+
github "dougzilla32/PromiseKit" "288f7fbabc0b33c558bf908a3a0770693223d4e0"

Sources/MKDirections+Promise.swift

+82-26
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,82 @@
1-
import MapKit
2-
#if !PMKCocoaPods
3-
import PromiseKit
4-
#endif
5-
6-
/**
7-
To import the `MKDirections` category:
8-
9-
use_frameworks!
10-
pod "PromiseKit/MapKit"
11-
12-
And then in your sources:
13-
14-
import PromiseKit
15-
*/
16-
extension MKDirections {
17-
/// Begins calculating the requested route information asynchronously.
18-
public func calculate() -> Promise<MKDirectionsResponse> {
19-
return Promise { calculate(completionHandler: $0.resolve) }
20-
}
21-
22-
/// Begins calculating the requested travel-time information asynchronously.
23-
public func calculateETA() -> Promise<MKETAResponse> {
24-
return Promise { calculateETA(completionHandler: $0.resolve) }
25-
}
26-
}
1+
import MapKit
2+
#if !PMKCocoaPods
3+
import PromiseKit
4+
#endif
5+
6+
/**
7+
To import the `MKDirections` category:
8+
9+
use_frameworks!
10+
pod "PromiseKit/MapKit"
11+
12+
And then in your sources:
13+
14+
import PromiseKit
15+
*/
16+
extension MKDirections {
17+
#if swift(>=4.2)
18+
/// Begins calculating the requested route information asynchronously.
19+
public func calculate() -> Promise<Response> {
20+
return Promise<Response>(cancellableTask: MKDirectionsTask(self)) { calculate(completionHandler: $0.resolve) }
21+
}
22+
23+
/// Begins calculating the requested travel-time information asynchronously.
24+
public func calculateETA() -> Promise<ETAResponse> {
25+
return Promise<ETAResponse>(cancellableTask: MKDirectionsTask(self)) { calculateETA(completionHandler: $0.resolve) }
26+
}
27+
#else
28+
/// Begins calculating the requested route information asynchronously.
29+
public func calculate() -> Promise<MKDirectionsResponse> {
30+
return Promise<MKDirectionsResponse>(cancellableTask: MKDirectionsTask(self)) { calculate(completionHandler: $0.resolve) }
31+
}
32+
33+
/// Begins calculating the requested travel-time information asynchronously.
34+
public func calculateETA() -> Promise<MKETAResponse> {
35+
return Promise<MKETAResponse>(cancellableTask: MKDirectionsTask(self)) { calculateETA(completionHandler: $0.resolve) }
36+
}
37+
#endif
38+
}
39+
40+
private class MKDirectionsTask: CancellableTask {
41+
let directions: MKDirections
42+
var cancelAttempted = false
43+
44+
init(_ directions: MKDirections) {
45+
self.directions = directions
46+
}
47+
48+
func cancel() {
49+
directions.cancel()
50+
cancelAttempted = true
51+
}
52+
53+
var isCancelled: Bool {
54+
return cancelAttempted && !directions.isCalculating
55+
}
56+
}
57+
58+
//////////////////////////////////////////////////////////// Cancellable wrappers
59+
60+
extension MKDirections {
61+
#if swift(>=4.2)
62+
/// Begins calculating the requested route information asynchronously.
63+
public func cancellableCalculate() -> CancellablePromise<Response> {
64+
return cancellable(calculate())
65+
}
66+
67+
/// Begins calculating the requested travel-time information asynchronously.
68+
public func cancellableCalculateETA() -> CancellablePromise<ETAResponse> {
69+
return cancellable(calculateETA())
70+
}
71+
#else
72+
/// Begins calculating the requested route information asynchronously.
73+
public func cancellableCalculate() -> CancellablePromise<MKDirectionsResponse> {
74+
return cancellable(calculate())
75+
}
76+
77+
/// Begins calculating the requested travel-time information asynchronously.
78+
public func cancellableCalculateETA() -> CancellablePromise<MKETAResponse> {
79+
return cancellable(calculateETA())
80+
}
81+
#endif
82+
}
+62-21
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,62 @@
1-
import MapKit
2-
#if !PMKCocoaPods
3-
import PromiseKit
4-
#endif
5-
6-
/**
7-
To import the `MKMapSnapshotter` category:
8-
9-
use_frameworks!
10-
pod "PromiseKit/MapKit"
11-
12-
And then in your sources:
13-
14-
import PromiseKit
15-
*/
16-
extension MKMapSnapshotter {
17-
/// Starts generating the snapshot using the options set in this object.
18-
public func start() -> Promise<MKMapSnapshot> {
19-
return Promise { start(completionHandler: $0.resolve) }
20-
}
21-
}
1+
import MapKit
2+
#if !PMKCocoaPods
3+
import PromiseKit
4+
#endif
5+
6+
/**
7+
To import the `MKMapSnapshotter` category:
8+
9+
use_frameworks!
10+
pod "PromiseKit/MapKit"
11+
12+
And then in your sources:
13+
14+
import PromiseKit
15+
*/
16+
extension MKMapSnapshotter {
17+
#if swift(>=4.2)
18+
/// Starts generating the snapshot using the options set in this object.
19+
public func start() -> Promise<Snapshot> {
20+
return Promise<Snapshot>(cancellableTask: MKMapSnapshotterTask(self)) { start(completionHandler: $0.resolve) }
21+
}
22+
#else
23+
/// Starts generating the snapshot using the options set in this object.
24+
public func start() -> Promise<MKMapSnapshot> {
25+
return Promise<MKMapSnapshot>(cancellableTask: MKMapSnapshotterTask(self)) { start(completionHandler: $0.resolve) }
26+
}
27+
#endif
28+
}
29+
30+
private class MKMapSnapshotterTask: CancellableTask {
31+
let snapshotter: MKMapSnapshotter
32+
var cancelAttempted = false
33+
34+
init(_ snapshotter: MKMapSnapshotter) {
35+
self.snapshotter = snapshotter
36+
}
37+
38+
func cancel() {
39+
snapshotter.cancel()
40+
cancelAttempted = true
41+
}
42+
43+
var isCancelled: Bool {
44+
return cancelAttempted && !snapshotter.isLoading
45+
}
46+
}
47+
48+
//////////////////////////////////////////////////////////// Cancellable wrapper
49+
50+
extension MKMapSnapshotter {
51+
#if swift(>=4.2)
52+
/// Starts generating the snapshot using the options set in this object.
53+
public func cancellableStart() -> CancellablePromise<Snapshot> {
54+
return cancellable(start())
55+
}
56+
#else
57+
/// Starts generating the snapshot using the options set in this object.
58+
public func cancellableStart() -> CancellablePromise<MKMapSnapshot> {
59+
return cancellable(start())
60+
}
61+
#endif
62+
}

Tests/TestMapKit.swift

+67
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,70 @@ class Test_MKSnapshotter_Swift: XCTestCase {
6161
waitForExpectations(timeout: 1, handler: nil)
6262
}
6363
}
64+
65+
//////////////////////////////////////////////////////////// Cancellation
66+
67+
extension Test_MKDirections_Swift {
68+
func test_cancel_directions_response() {
69+
let ex = expectation(description: "")
70+
71+
class MockDirections: MKDirections {
72+
override func calculate(completionHandler: @escaping MKDirectionsHandler) {
73+
completionHandler(MKDirectionsResponse(), nil)
74+
}
75+
}
76+
77+
let rq = MKDirectionsRequest()
78+
let directions = MockDirections(request: rq)
79+
80+
directions.cancellableCalculate().done { _ in
81+
XCTFail()
82+
}.catch(policy: .allErrors) {
83+
$0.isCancelled ? ex.fulfill() : XCTFail()
84+
}.cancel()
85+
86+
waitForExpectations(timeout: 1, handler: nil)
87+
}
88+
89+
90+
func test_cancel_ETA_response() {
91+
let ex = expectation(description: "")
92+
93+
class MockDirections: MKDirections {
94+
override func calculateETA(completionHandler: @escaping MKETAHandler) {
95+
completionHandler(MKETAResponse(), nil)
96+
}
97+
}
98+
99+
let rq = MKDirectionsRequest()
100+
MockDirections(request: rq).cancellableCalculateETA().done { _ in
101+
XCTFail()
102+
}.catch(policy: .allErrors) {
103+
$0.isCancelled ? ex.fulfill() : XCTFail()
104+
}.cancel()
105+
106+
waitForExpectations(timeout: 1, handler: nil)
107+
}
108+
109+
}
110+
111+
extension Test_MKSnapshotter_Swift {
112+
func test_cancel() {
113+
let ex = expectation(description: "")
114+
115+
class MockSnapshotter: MKMapSnapshotter {
116+
override func start(completionHandler: @escaping MKMapSnapshotCompletionHandler) {
117+
completionHandler(MKMapSnapshot(), nil)
118+
}
119+
}
120+
121+
let snapshotter = MockSnapshotter()
122+
snapshotter.cancellableStart().done { _ in
123+
XCTFail()
124+
}.catch(policy: .allErrors) {
125+
$0.isCancelled ? ex.fulfill() : XCTFail()
126+
}.cancel()
127+
128+
waitForExpectations(timeout: 1, handler: nil)
129+
}
130+
}

0 commit comments

Comments
 (0)