Skip to content

Commit 9f96ec4

Browse files
committed
'Cancel' for PromiseKit -- provides the ability to cancel promises and promise chains
1 parent de16bbe commit 9f96ec4

File tree

5 files changed

+81
-5
lines changed

5 files changed

+81
-5
lines changed

Cartfile

Lines changed: 2 additions & 1 deletion
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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
github "mxcl/PromiseKit" "6.3.3"
1+
github "dougzilla32/PromiseKit" "087b3cf470890ff9ea841212e2f3e285fecf3988"

Sources/SKProductsRequest+Promise.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension SKProductsRequest {
2020
- Returns: A promise that fulfills if the request succeeds.
2121
*/
2222
public func start(_: PMKNamespacer) -> Promise<SKProductsResponse> {
23-
let proxy = SKDelegate()
23+
let proxy = SKDelegate(request: self)
2424
delegate = proxy
2525
proxy.retainCycle = proxy
2626
start()
@@ -29,10 +29,17 @@ extension SKProductsRequest {
2929
}
3030

3131

32-
fileprivate class SKDelegate: NSObject, SKProductsRequestDelegate {
32+
fileprivate class SKDelegate: NSObject, SKProductsRequestDelegate, CancellableTask {
3333
let (promise, seal) = Promise<SKProductsResponse>.pending()
34+
let request: SKRequest
3435
var retainCycle: SKDelegate?
3536

37+
init(request: SKRequest) {
38+
self.request = request
39+
super.init()
40+
promise.setCancellableTask(self, reject: seal.reject)
41+
}
42+
3643
@objc fileprivate func request(_ request: SKRequest, didFailWithError error: Error) {
3744
seal.reject(error)
3845
retainCycle = nil
@@ -42,6 +49,14 @@ fileprivate class SKDelegate: NSObject, SKProductsRequestDelegate {
4249
seal.fulfill(response)
4350
retainCycle = nil
4451
}
52+
53+
var isCancelled = false
54+
55+
func cancel() {
56+
request.cancel()
57+
retainCycle = nil
58+
isCancelled = true
59+
}
4560
}
4661

4762
// perhaps one day Apple will actually make their errors into Errors…
@@ -50,3 +65,16 @@ fileprivate class SKDelegate: NSObject, SKProductsRequestDelegate {
5065
// return true
5166
// }
5267
//}
68+
69+
//////////////////////////////////////////////////////////// Cancellable wrapper
70+
71+
extension SKProductsRequest {
72+
/**
73+
Sends the request to the Apple App Store.
74+
75+
- Returns: A cancellable promise that fulfills if the request succeeds.
76+
*/
77+
public func cancellableStart(_: PMKNamespacer) -> CancellablePromise<SKProductsResponse> {
78+
return cancellable(start(.promise))
79+
}
80+
}

Sources/SKReceiptRefreshRequest+Promise.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ extension SKReceiptRefreshRequest {
99
}
1010
}
1111

12-
private class ReceiptRefreshObserver: NSObject, SKRequestDelegate {
12+
private class ReceiptRefreshObserver: NSObject, SKRequestDelegate, CancellableTask {
1313
let (promise, seal) = Promise<SKReceiptRefreshRequest>.pending()
1414
let request: SKReceiptRefreshRequest
1515
var retainCycle: ReceiptRefreshObserver?
@@ -32,4 +32,21 @@ private class ReceiptRefreshObserver: NSObject, SKRequestDelegate {
3232
seal.reject(error)
3333
retainCycle = nil
3434
}
35+
36+
var isCancelled = false
37+
38+
func cancel() {
39+
request.cancel()
40+
retainCycle = nil
41+
isCancelled = true
42+
}
43+
}
44+
45+
//////////////////////////////////////////////////////////// Cancellation
46+
47+
extension SKReceiptRefreshRequest {
48+
public func cancellablePromise() -> CancellablePromise<SKReceiptRefreshRequest> {
49+
let rro = ReceiptRefreshObserver(request: self)
50+
return CancellablePromise(task: rro, promise: rro.promise, resolver: rro.seal)
51+
}
3552
}

Tests/TestStoreKit.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,33 @@ class SKProductsRequestTests: XCTestCase {
2020
waitForExpectations(timeout: 1, handler: nil)
2121
}
2222
}
23+
24+
//////////////////////////////////////////////////////////// Cancellation
25+
26+
extension SKProductsRequestTests {
27+
func testCancel() {
28+
class MockProductsRequest: SKProductsRequest {
29+
var isCancelled = false
30+
31+
override func start() {
32+
after(seconds: 0.1).done {
33+
if !self.isCancelled {
34+
self.delegate?.productsRequest(self, didReceive: SKProductsResponse())
35+
}
36+
}
37+
}
38+
}
39+
40+
let ex = expectation(description: "")
41+
42+
let request = MockProductsRequest()
43+
request.cancellableStart(.promise).done { _ in
44+
XCTFail()
45+
}.catch(policy: .allErrors) {
46+
$0.isCancelled ? ex.fulfill() : XCTFail()
47+
}.cancel()
48+
request.isCancelled = true
49+
50+
waitForExpectations(timeout: 1, handler: nil)
51+
}
52+
}

0 commit comments

Comments
 (0)