diff --git a/Cartfile b/Cartfile index 2bfea98..cdd4145 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1,4 @@ -github "mxcl/PromiseKit" ~> 6.0 +#github "mxcl/PromiseKit" ~> 6.0 +github "dougzilla32/PromiseKit" "PMKCancel" +#github "PromiseKit/Cancel" ~> 1.0 +github "dougzilla32/Cancel" ~> 1.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index a1be206..e1cadd5 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1,2 @@ -github "mxcl/PromiseKit" "6.3.3" +github "dougzilla32/Cancel" "1.0.0" +github "dougzilla32/PromiseKit" "a0217bd7b69af68237dcdeee0197e63259b9d445" diff --git a/PMKStoreKit.xcodeproj/project.pbxproj b/PMKStoreKit.xcodeproj/project.pbxproj index 5cbb415..477586a 100644 --- a/PMKStoreKit.xcodeproj/project.pbxproj +++ b/PMKStoreKit.xcodeproj/project.pbxproj @@ -204,6 +204,7 @@ ); inputPaths = ( PromiseKit, + PMKCancel, ); name = "Embed Carthage Frameworks"; outputPaths = ( diff --git a/Sources/SKProductsRequest+Promise.swift b/Sources/SKProductsRequest+Promise.swift index 5497fcd..f2e2fc0 100644 --- a/Sources/SKProductsRequest+Promise.swift +++ b/Sources/SKProductsRequest+Promise.swift @@ -1,5 +1,6 @@ import StoreKit #if !PMKCocoaPods +import PMKCancel import PromiseKit #endif @@ -50,3 +51,40 @@ fileprivate class SKDelegate: NSObject, SKProductsRequestDelegate { // return true // } //} + +//////////////////////////////////////////////////////////// Cancellation + +extension SKProductsRequest { + /** + Sends the request to the Apple App Store. + + - Returns: A cancellable promise that fulfills if the request succeeds. + */ + public func startCC(_: PMKNamespacer) -> CancellablePromise { + let proxy = SKDelegate() + delegate = proxy + proxy.retainCycle = proxy + let cp = CancellablePromise(task: SKRequestTask(self, proxy), promise: proxy.promise, resolver: proxy.seal) + + start() + return cp + } +} + +fileprivate class SKRequestTask: CancellableTask { + let request: SKRequest + let proxy: SKDelegate + + init(_ request: SKRequest, _ proxy: SKDelegate) { + self.request = request + self.proxy = proxy + } + + var isCancelled = false + + func cancel() { + request.cancel() + proxy.retainCycle = nil + isCancelled = true + } +} diff --git a/Sources/SKReceiptRefreshRequest+Promise.swift b/Sources/SKReceiptRefreshRequest+Promise.swift index 3bbc784..cd5393c 100644 --- a/Sources/SKReceiptRefreshRequest+Promise.swift +++ b/Sources/SKReceiptRefreshRequest+Promise.swift @@ -1,4 +1,5 @@ #if !PMKCocoaPods +import PMKCancel import PromiseKit #endif import StoreKit @@ -9,7 +10,7 @@ extension SKReceiptRefreshRequest { } } -private class ReceiptRefreshObserver: NSObject, SKRequestDelegate { +private class ReceiptRefreshObserver: NSObject, SKRequestDelegate, CancellableTask { let (promise, seal) = Promise.pending() let request: SKReceiptRefreshRequest var retainCycle: ReceiptRefreshObserver? @@ -32,4 +33,21 @@ private class ReceiptRefreshObserver: NSObject, SKRequestDelegate { seal.reject(error) retainCycle = nil } + + var isCancelled = false + + func cancel() { + request.cancel() + retainCycle = nil + isCancelled = true + } +} + +//////////////////////////////////////////////////////////// Cancellation + +extension SKReceiptRefreshRequest { + public func promiseCC() -> CancellablePromise { + let rro = ReceiptRefreshObserver(request: self) + return CancellablePromise(task: rro, promise: rro.promise, resolver: rro.seal) + } } diff --git a/Tests/TestStoreKit.swift b/Tests/TestStoreKit.swift index e40ae84..ea3a937 100644 --- a/Tests/TestStoreKit.swift +++ b/Tests/TestStoreKit.swift @@ -1,3 +1,4 @@ +import PMKCancel import PMKStoreKit import PromiseKit import StoreKit @@ -20,3 +21,33 @@ class SKProductsRequestTests: XCTestCase { waitForExpectations(timeout: 1, handler: nil) } } + +//////////////////////////////////////////////////////////// Cancellation + +extension SKProductsRequestTests { + func testCancel() { + class MockProductsRequest: SKProductsRequest { + var isCancelled = false + + override func start() { + after(seconds: 0.1).done { + if !self.isCancelled { + self.delegate?.productsRequest(self, didReceive: SKProductsResponse()) + } + } + } + } + + let ex = expectation(description: "") + + let request = MockProductsRequest() + request.startCC(.promise).done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + request.isCancelled = true + + waitForExpectations(timeout: 1, handler: nil) + } +}