From a69d96ffa03c94512aaed688fb2f99cac79f8b9f Mon Sep 17 00:00:00 2001 From: Doug Date: Wed, 18 Jul 2018 17:44:51 -0700 Subject: [PATCH 1/3] 'Cancel' for PromiseKit --- Cartfile | 2 + Cartfile.resolved | 3 +- Sources/SKProductsRequest+Promise.swift | 38 +++++++++++++++++++ Sources/SKReceiptRefreshRequest+Promise.swift | 20 +++++++++- Tests/TestStoreKit.swift | 31 +++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index 2bfea98..c2d2433 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1,3 @@ github "mxcl/PromiseKit" ~> 6.0 +#github "PromiseKit/Cancel" ~> 1.0 +github "dougzilla32/Cancel" ~> 1.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index a1be206..6d512a1 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1,2 @@ -github "mxcl/PromiseKit" "6.3.3" +github "dougzilla32/Cancel" "1.0.0" +github "mxcl/PromiseKit" "6.3.4" 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) + } +} From bebd21c80a119e12134694329d144a7422582fee Mon Sep 17 00:00:00 2001 From: Doug Date: Wed, 18 Jul 2018 23:11:34 -0700 Subject: [PATCH 2/3] 'Cancel' for PromiseKit -- + for PMKCancel, change watchOS deployment target from 3.0 to 2.0 + include PMKCancel in the 'Embed Carthage Frameworks' build phase script for all extensions + include PMKCancel in SPM config for Alamofire and Foundation --- PMKStoreKit.xcodeproj/project.pbxproj | 1 + 1 file changed, 1 insertion(+) 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 = ( From 015475812d3708b7a4cdf927ec3337d1db6dc5cc Mon Sep 17 00:00:00 2001 From: dougzilla Date: Thu, 19 Jul 2018 15:07:37 -0700 Subject: [PATCH 3/3] 'Cancel' for PromiseKit -- for all Extensions point Cartfile at the forked version of PromiseKit --- Cartfile | 3 ++- Cartfile.resolved | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index c2d2433..cdd4145 100644 --- a/Cartfile +++ b/Cartfile @@ -1,3 +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 6d512a1..e1cadd5 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,2 +1,2 @@ github "dougzilla32/Cancel" "1.0.0" -github "mxcl/PromiseKit" "6.3.4" +github "dougzilla32/PromiseKit" "a0217bd7b69af68237dcdeee0197e63259b9d445"