瀏覽代碼

Modified InAppProducrPurchaseRequest and SwiftyStoreKit to gather and return multiple results at once as needed when restoring purchases.

Andrea Bizzotto 9 年之前
父節點
當前提交
faf77cbf79

+ 10 - 9
SwiftyStoreDemo/ViewController.swift

@@ -154,18 +154,19 @@ extension ViewController {
         }
     }
     
-    func alertForRestorePurchases(result: SwiftyStoreKit.RestoreResult) -> UIAlertController {
-        
-        switch result {
-        case .Success(let productId):
-            print("Restore Success: \(productId)")
+    func alertForRestorePurchases(result: SwiftyStoreKit.RestoreResults) -> UIAlertController {
+
+        if result.restoreFailedProducts.count > 0 {
+            print("Restore Failed: \(result.restoreFailedProducts)")
+            return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
+        }
+        else if result.restoredProductIds.count > 0 {
+            print("Restore Success: \(result.restoredProductIds)")
             return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
-        case .NothingToRestore:
+        }
+        else {
             print("Nothing to Restore")
             return alertWithTitle("Nothing to restore", message: "No previous purchases were found")
-        case .Error(let error):
-            print("Restore Failed: \(error)")
-            return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
         }
     }
 

+ 17 - 17
SwiftyStoreKit/InAppProductPurchaseRequest.swift

@@ -31,11 +31,10 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
     enum TransactionResult {
         case Purchased(productId: String)
         case Restored(productId: String)
-        case NothingToRestore
         case Failed(error: NSError)
     }
     
-    typealias RequestCallback = (result: TransactionResult) -> ()
+    typealias RequestCallback = (results: [TransactionResult]) -> ()
     private let callback: RequestCallback
     private var purchases : [PaymentTransactionState: [String]] = [:]
 
@@ -88,6 +87,8 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
     // MARK: SKPaymentTransactionObserver
     func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
         
+        var transactionResults: [TransactionResult] = []
+        
         for transaction in transactions {
 
             #if os(iOS)
@@ -98,22 +99,16 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
 
             switch transactionState {
             case .Purchased:
-                dispatch_async(dispatch_get_main_queue()) {
-                    self.callback(result: .Purchased(productId: transaction.payment.productIdentifier))
-                }
+                transactionResults.append(.Purchased(productId: transaction.payment.productIdentifier))
                 paymentQueue.finishTransaction(transaction)
             case .Failed:
-                dispatch_async(dispatch_get_main_queue()) {
-                    // It appears that in some edge cases transaction.error is nil here. Since returning an associated error is
-                    // mandatory, return a default one if needed
-                    let altError = NSError(domain: SKErrorDomain, code: 0, userInfo: [ NSLocalizedDescriptionKey: "Unknown error" ])
-                    self.callback(result: .Failed(error: transaction.error ?? altError))
-                }
+                // It appears that in some edge cases transaction.error is nil here. Since returning an associated error is
+                // mandatory, return a default one if needed
+                let altError = NSError(domain: SKErrorDomain, code: 0, userInfo: [ NSLocalizedDescriptionKey: "Unknown error" ])
+                transactionResults.append(.Failed(error: transaction.error ?? altError))
                 paymentQueue.finishTransaction(transaction)
             case .Restored:
-                dispatch_async(dispatch_get_main_queue()) {
-                    self.callback(result: .Restored(productId: transaction.payment.productIdentifier))
-                }
+                transactionResults.append(.Restored(productId: transaction.payment.productIdentifier))
                 paymentQueue.finishTransaction(transaction)
             case .Purchasing:
                 // In progress: do nothing
@@ -129,6 +124,11 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
                 purchases[transactionState] = [ transaction.payment.productIdentifier ]
             }
         }
+        if transactionResults.count > 0 {
+            dispatch_async(dispatch_get_main_queue()) {
+                self.callback(results: transactionResults)
+            }
+        }
     }
     
     func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
@@ -138,19 +138,19 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
     func paymentQueue(queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: NSError) {
         
         dispatch_async(dispatch_get_main_queue()) {
-            self.callback(result: .Failed(error: error))
+            self.callback(results: [.Failed(error: error)])
         }
     }
 
     func paymentQueueRestoreCompletedTransactionsFinished(queue: SKPaymentQueue) {
         if let product = self.product, productIdentifier = product._productIdentifier {
-            self.callback(result: .Restored(productId: productIdentifier))
+            self.callback(results: [.Restored(productId: productIdentifier)])
             return
         }
         // This method will be called after all purchases have been restored (includes the case of no purchases)
         guard let restored = purchases[.Restored] where restored.count > 0 else {
             
-            self.callback(result: .NothingToRestore)
+            self.callback(results: [])
             return
         }
         //print("\(restored)")

+ 27 - 23
SwiftyStoreKit/SwiftyStoreKit.swift

@@ -58,10 +58,9 @@ public class SwiftyStoreKit {
         case Success(product: SKProduct)
         case Error(error: ErrorType)
     }
-    public enum RestoreResult {
-        case Success(productId: String)
-        case Error(error: ErrorType)
-        case NothingToRestore
+    public struct RestoreResults {
+        public let restoredProductIds: [String]
+        public let restoreFailedProducts: [(ErrorType, String?)]
     }
     public enum RefreshReceiptResult {
         case Success
@@ -115,13 +114,14 @@ public class SwiftyStoreKit {
         }
     }
     
-    public class func restorePurchases(completion: (result: RestoreResult) -> ()) {
+    public class func restorePurchases(completion: (results: RestoreResults) -> ()) {
 
-        sharedInstance.restoreRequest = InAppProductPurchaseRequest.restorePurchases() { result in
+        // Called multiple
+        sharedInstance.restoreRequest = InAppProductPurchaseRequest.restorePurchases() { results in
         
             sharedInstance.restoreRequest = nil
-            let returnValue = sharedInstance.processRestoreResult(result)
-            completion(result: returnValue)
+            let results = sharedInstance.processRestoreResults(results)
+            completion(results: results)
         }
     }
 
@@ -177,13 +177,15 @@ public class SwiftyStoreKit {
             return
         }
 
-        inflightPurchases[productIdentifier] = InAppProductPurchaseRequest.startPayment(product) { result in
+        inflightPurchases[productIdentifier] = InAppProductPurchaseRequest.startPayment(product) { results in
 
             if let productIdentifier = product._productIdentifier {
                 self.inflightPurchases[productIdentifier] = nil
             }
-            let returnValue = self.processPurchaseResult(result)
-            completion(result: returnValue)
+            if let purchasedProductTransaction = results.first {
+                let returnValue = self.processPurchaseResult(purchasedProductTransaction)
+                completion(result: returnValue)
+            }
         }
     }
 
@@ -195,24 +197,26 @@ public class SwiftyStoreKit {
             return .Error(error: .Failed(error: error))
         case .Restored(let productId):
             return .Error(error: .Failed(error: storeInternalError(code: InternalErrorCode.RestoredPurchaseWhenPurchasing.rawValue, description: "Cannot restore product \(productId) from purchase path")))
-        case .NothingToRestore:
-            return .Error(error: .Failed(error: storeInternalError(code: InternalErrorCode.NothingToRestoreWhenPurchasing.rawValue, description: "Cannot restore product from purchase path")))
         }
     }
     
-    private func processRestoreResult(result: InAppProductPurchaseRequest.TransactionResult) -> RestoreResult {
-        switch result {
-        case .Purchased(let productId):
-            return .Error(error: storeInternalError(code: InternalErrorCode.PurchasedWhenRestoringPurchase.rawValue, description: "Cannot purchase product \(productId) from restore purchases path"))
-        case .Failed(let error):
-            return .Error(error: error)
-        case .Restored(let productId):
-            return .Success(productId: productId)
-        case .NothingToRestore:
-            return .NothingToRestore
+    private func processRestoreResults(results: [InAppProductPurchaseRequest.TransactionResult]) -> RestoreResults {
+        var restoredProductIds: [String] = []
+        var restoreFailedProducts: [(ErrorType, String?)] = []
+        for result in results {
+            switch result {
+            case .Purchased(let productId):
+                restoreFailedProducts.append((storeInternalError(code: InternalErrorCode.PurchasedWhenRestoringPurchase.rawValue, description: "Cannot purchase product \(productId) from restore purchases path"), productId))
+            case .Failed(let error):
+                restoreFailedProducts.append((error, nil))
+            case .Restored(let productId):
+                restoredProductIds.append(productId)
+            }
         }
+        return RestoreResults(restoredProductIds: restoredProductIds, restoreFailedProducts: restoreFailedProducts)
     }
     
+    
     // http://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/
     private func requestProduct(productId: String, completion: (result: (() throws -> SKProduct)) -> ()) -> () {
         

+ 12 - 11
SwiftyStoreOSXDemo/ViewController.swift

@@ -61,9 +61,9 @@ class ViewController: NSViewController {
 
     @IBAction func restorePurchases(sender: AnyObject?) {
 
-        SwiftyStoreKit.restorePurchases() { result in
+        SwiftyStoreKit.restorePurchases() { results in
             
-            self.showAlert(self.alertForRestorePurchases(result))
+            self.showAlert(self.alertForRestorePurchases(results))
         }
     }
 
@@ -140,21 +140,22 @@ extension ViewController {
         }
     }
     
-    func alertForRestorePurchases(result: SwiftyStoreKit.RestoreResult) -> NSAlert {
+    func alertForRestorePurchases(result: SwiftyStoreKit.RestoreResults) -> NSAlert {
         
-        switch result {
-        case .Success(let productId):
-            print("Restore Success: \(productId)")
+        if result.restoreFailedProducts.count > 0 {
+            print("Restore Failed: \(result.restoreFailedProducts)")
+            return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
+        }
+        else if result.restoredProductIds.count > 0 {
+            print("Restore Success: \(result.restoredProductIds)")
             return alertWithTitle("Purchases Restored", message: "All purchases have been restored")
-        case .NothingToRestore:
+        }
+        else {
             print("Nothing to Restore")
             return alertWithTitle("Nothing to restore", message: "No previous purchases were found")
-        case .Error(let error):
-            print("Restore Failed: \(error)")
-            return alertWithTitle("Restore failed", message: "Unknown error. Please contact support")
         }
     }
-
+    
     func alertForVerifyReceipt(result: SwiftyStoreKit.VerifyReceiptResult) -> NSAlert {
 
         switch result {