浏览代码

Merge pull request #538 from RomanPodymov/develop

Cancellable requests
Samuel Spencer 5 年之前
父节点
当前提交
cca046f355

+ 3 - 1
SwiftyStoreKit/InAppProductQueryRequest.swift

@@ -26,11 +26,13 @@ import StoreKit
 
 typealias InAppProductRequestCallback = (RetrieveResults) -> Void
 
-protocol InAppProductRequest: class {
+public protocol InAppRequest: class {
     func start()
     func cancel()
 }
 
+protocol InAppProductRequest: InAppRequest { }
+
 class InAppProductQueryRequest: NSObject, InAppProductRequest, SKProductsRequestDelegate {
 
     private let callback: InAppProductRequestCallback

+ 5 - 1
SwiftyStoreKit/InAppReceiptRefreshRequest.swift

@@ -26,7 +26,7 @@
 import StoreKit
 import Foundation
 
-class InAppReceiptRefreshRequest: NSObject, SKRequestDelegate {
+class InAppReceiptRefreshRequest: NSObject, SKRequestDelegate, InAppRequest {
 
     enum ResultType {
         case success
@@ -60,6 +60,10 @@ class InAppReceiptRefreshRequest: NSObject, SKRequestDelegate {
         self.refreshReceiptRequest.start()
     }
 
+    func cancel() {
+        self.refreshReceiptRequest.cancel()
+    }
+    
     func requestDidFinish(_ request: SKRequest) {
         /*if let resoreRequest = request as? SKReceiptRefreshRequest {
          let receiptProperties = resoreRequest.receiptProperties ?? [:]

+ 7 - 3
SwiftyStoreKit/InAppReceiptVerificator.swift

@@ -49,12 +49,13 @@ class InAppReceiptVerificator: NSObject {
      *  - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability)
      *  - Parameter completion: handler for result
      */
+    @discardableResult
     public func verifyReceipt(using validator: ReceiptValidator,
                               forceRefresh: Bool,
                               refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh,
-                              completion: @escaping (VerifyReceiptResult) -> Void) {
+                              completion: @escaping (VerifyReceiptResult) -> Void) -> InAppRequest? {
         
-        fetchReceipt(forceRefresh: forceRefresh, refresh: refresh) { result in
+        return fetchReceipt(forceRefresh: forceRefresh, refresh: refresh) { result in
             switch result {
             case .success(let receiptData):
                 self.verify(receiptData: receiptData, using: validator, completion: completion)
@@ -72,12 +73,14 @@ class InAppReceiptVerificator: NSObject {
      *  - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability)
      *  - Parameter completion: handler for result
      */
+    @discardableResult
     public func fetchReceipt(forceRefresh: Bool,
                              refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh,
-                             completion: @escaping (FetchReceiptResult) -> Void) {
+                             completion: @escaping (FetchReceiptResult) -> Void) -> InAppRequest? {
 
         if let receiptData = appStoreReceiptData, forceRefresh == false {
             completion(.success(receiptData: receiptData))
+            return nil
         } else {
             
             receiptRefreshRequest = refresh(nil) { result in
@@ -95,6 +98,7 @@ class InAppReceiptVerificator: NSObject {
                     completion(.error(error: .networkError(error: e)))
                 }
             }
+            return receiptRefreshRequest
         }
     }
     

+ 4 - 1
SwiftyStoreKit/ProductsInfoController.swift

@@ -51,7 +51,8 @@ class ProductsInfoController: NSObject {
     // As we can have multiple inflight requests, we store them in a dictionary by product ids
     private var inflightRequests: [Set<String>: InAppProductQuery] = [:]
 
-    func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) {
+    @discardableResult
+    func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) -> InAppProductRequest {
 
         if inflightRequests[productIds] == nil {
             let request = inAppProductRequestBuilder.request(productIds: productIds) { results in
@@ -68,8 +69,10 @@ class ProductsInfoController: NSObject {
             }
             inflightRequests[productIds] = InAppProductQuery(request: request, completionHandlers: [completion])
             request.start()
+            return request
         } else {
             inflightRequests[productIds]!.completionHandlers.append(completion)
+            return inflightRequests[productIds]!.request
         }
     }
 }

+ 14 - 10
SwiftyStoreKit/SwiftyStoreKit.swift

@@ -42,13 +42,13 @@ public class SwiftyStoreKit {
     }
 
     // MARK: private methods
-    fileprivate func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) {
+    fileprivate func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) -> InAppProductRequest {
         return productsInfoController.retrieveProductsInfo(productIds, completion: completion)
     }
     
-    fileprivate func purchaseProduct(_ productId: String, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping ( PurchaseResult) -> Void) {
+    fileprivate func purchaseProduct(_ productId: String, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping ( PurchaseResult) -> Void) -> InAppProductRequest {
 
-        retrieveProductsInfo(Set([productId])) { result -> Void in
+        return retrieveProductsInfo(Set([productId])) { result -> Void in
             if let product = result.retrievedProducts.first {
                 self.purchase(product: product, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox, completion: completion)
             } else if let error = result.error {
@@ -143,7 +143,8 @@ extension SwiftyStoreKit {
      *  - Parameter productIds: The set of product identifiers to retrieve corresponding products for
      *  - Parameter completion: handler for result
      */
-    public class func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) {
+    @discardableResult
+    public class func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) -> InAppRequest {
 
         return sharedInstance.retrieveProductsInfo(productIds, completion: completion)
     }
@@ -156,9 +157,10 @@ extension SwiftyStoreKit {
      *  - Parameter applicationUsername: an opaque identifier for the user’s account on your system
      *  - Parameter completion: handler for result
      */
-    public class func purchaseProduct(_ productId: String, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping (PurchaseResult) -> Void) {
+    @discardableResult
+    public class func purchaseProduct(_ productId: String, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping (PurchaseResult) -> Void) -> InAppRequest {
 
-        sharedInstance.purchaseProduct(productId, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox, completion: completion)
+        return sharedInstance.purchaseProduct(productId, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox, completion: completion)
     }
     
     /**
@@ -255,9 +257,10 @@ extension SwiftyStoreKit {
      *  - Parameter forceRefresh: If true, refreshes the receipt even if one already exists.
      *  - Parameter completion: handler for result
      */
-    public class func verifyReceipt(using validator: ReceiptValidator, forceRefresh: Bool = false, completion: @escaping (VerifyReceiptResult) -> Void) {
+    @discardableResult
+    public class func verifyReceipt(using validator: ReceiptValidator, forceRefresh: Bool = false, completion: @escaping (VerifyReceiptResult) -> Void) -> InAppRequest? {
 
-        sharedInstance.receiptVerificator.verifyReceipt(using: validator, forceRefresh: forceRefresh, completion: completion)
+        return sharedInstance.receiptVerificator.verifyReceipt(using: validator, forceRefresh: forceRefresh, completion: completion)
     }
 
     /**
@@ -265,9 +268,10 @@ extension SwiftyStoreKit {
      *  - Parameter forceRefresh: If true, refreshes the receipt even if one already exists.
      *  - Parameter completion: handler for result
      */
-    public class func fetchReceipt(forceRefresh: Bool, completion: @escaping (FetchReceiptResult) -> Void) {
+    @discardableResult
+    public class func fetchReceipt(forceRefresh: Bool, completion: @escaping (FetchReceiptResult) -> Void) -> InAppRequest? {
     
-        sharedInstance.receiptVerificator.fetchReceipt(forceRefresh: forceRefresh, completion: completion)
+        return sharedInstance.receiptVerificator.fetchReceipt(forceRefresh: forceRefresh, completion: completion)
     }
     
     /**

+ 16 - 8
SwiftyStoreKitTests/InAppReceiptVerificatorTests.swift

@@ -76,7 +76,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
         
         var refreshCalled = false
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             refreshCalled = true
             return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
@@ -84,6 +84,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         }, completion: { _ in
             
         })
+        XCTAssertNotNil(request)
         XCTAssertTrue(refreshCalled)
     }
 
@@ -95,7 +96,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
         
         var refreshCalled = false
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             refreshCalled = true
             return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
@@ -103,6 +104,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         }, completion: { _ in
             
         })
+        XCTAssertNotNil(request)
         XCTAssertTrue(refreshCalled)
     }
     
@@ -115,7 +117,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
         
         var refreshCalled = false
-        verificator.verifyReceipt(using: validator, forceRefresh: true, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: true, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             refreshCalled = true
             return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
@@ -123,6 +125,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         }, completion: { _ in
             
         })
+        XCTAssertNotNil(request)
         XCTAssertTrue(refreshCalled)
     }
 
@@ -132,7 +135,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
         let refreshError = NSError(domain: "", code: 0, userInfo: nil)
         
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             callback(.error(e: refreshError))
             return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
@@ -141,6 +144,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
             
             XCTAssertEqual(result, VerifyReceiptResult.error(error: ReceiptError.networkError(error: refreshError)))
         })
+        XCTAssertNotNil(request)
     }
 
     func testVerifyReceipt_when_appStoreReceiptURLIsNil_refreshCallbackSuccess_receiptDataNotWritten_then_errorNoReceiptData_validateNotCalled() {
@@ -148,7 +152,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let validator = TestReceiptValidator()
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
         
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             callback(.success)
             return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
@@ -157,6 +161,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
 
             XCTAssertEqual(result, VerifyReceiptResult.error(error: ReceiptError.noReceiptData))
         })
+        XCTAssertNotNil(request)
         XCTAssertFalse(validator.validateCalled)
     }
 
@@ -167,7 +172,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let validator = TestReceiptValidator()
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
         
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             writeReceiptData(to: testReceiptURL)
             callback(.success)
@@ -177,6 +182,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
             
             XCTAssertEqual(result, VerifyReceiptResult.error(error: ReceiptError.noReceiptData))
         })
+        XCTAssertNotNil(request)
         XCTAssertFalse(validator.validateCalled)
         removeReceiptData(at: testReceiptURL)
     }
@@ -188,7 +194,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let validator = TestReceiptValidator()
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
         
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             writeReceiptData(to: testReceiptURL)
             callback(.success)
@@ -197,6 +203,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         }, completion: { _ in
             
         })
+        XCTAssertNil(request)
         XCTAssertTrue(validator.validateCalled)
         removeReceiptData(at: testReceiptURL)
     }
@@ -210,7 +217,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         let validator = TestReceiptValidator()
         let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
         
-        verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
+        let request = verificator.verifyReceipt(using: validator, forceRefresh: false, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
             
             XCTFail("refresh should not be called if we already have a receipt")
             return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
@@ -218,6 +225,7 @@ class InAppReceiptVerificatorTests: XCTestCase {
         }, completion: { _ in
             
         })
+        XCTAssertNil(request)
         XCTAssertTrue(validator.validateCalled)
         removeReceiptData(at: testReceiptURL)
     }