Selaa lähdekoodia

Implement non-atomic complete transactions.
Update all purchase APIs to return a Product type when successful

Andrea Bizzotto 8 vuotta sitten
vanhempi
commit
27aa8a687e

+ 16 - 12
README.md

@@ -33,13 +33,17 @@ SwiftyStoreKit supports this by calling `completeTransactions()` when the app st
 ```swift
 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
 
-	SwiftyStoreKit.completeTransactions() { completedTransactions in
+	SwiftyStoreKit.completeTransactions(atomically: true) { products in
 	
-	    for completedTransaction in completedTransactions {
+	    for product in products {
 	
-	        if completedTransaction.transactionState == .purchased || completedTransaction.transactionState == .restored {
+	        if product.transaction.transactionState == .purchased || product.transaction.transactionState == .restored {
 	
-	            print("purchased: \(completedTransaction.productId)")
+               if product.needsFinishTransaction {
+                   // Deliver content from server, then:
+                   SwiftyStoreKit.finishTransaction(product.transaction)
+               }
+               print("purchased: \(product)")
 	        }
 	    }
 	}
@@ -71,8 +75,8 @@ SwiftyStoreKit.retrieveProductsInfo(["com.musevisions.SwiftyStoreKit.Purchase1"]
 ```swift
 SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", atomically: true) { result in
     switch result {
-    case .success(let productId, _):
-        print("Purchase Success: \(productId)")
+    case .success(let product):
+        print("Purchase Success: \(product.productId)")
     case .error(let error):
         print("Purchase Failed: \(error)")
     }
@@ -84,12 +88,12 @@ SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", atomi
 ```swift
 SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", atomically: false) { result in
     switch result {
-    case .success(let productId, let transaction):
+    case .success(let product):
         // fetch content from your server, then:
-        if let transaction = transaction {
-            SwiftyStoreKit.finishTransaction(transaction)
+        if product.needsFinishTransaction {
+            SwiftyStoreKit.finishTransaction(product.transaction)
         }
-        print("Purchase Success: \(productId)")
+        print("Purchase Success: \(product.productId)")
     case .error(let error):
         print("Purchase Failed: \(error)")
     }
@@ -124,8 +128,8 @@ SwiftyStoreKit.restorePurchases(atomically: false) { results in
     else if results.restoredProducts.count > 0 {
         for product in results.restoredProducts {
             // fetch content from your server, then:
-            if let transaction = product.transaction {
-                SwiftyStoreKit.finishTransaction(transaction)
+            if product.needsFinishTransaction {
+                SwiftyStoreKit.finishTransaction(product.transaction)
             }
         }
         print("Restore Success: \(results.restoredProducts)")

+ 8 - 4
SwiftyStoreKit-iOS-Demo/AppDelegate.swift

@@ -55,13 +55,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     
     func completeIAPTransactions() {
         
-        SwiftyStoreKit.completeTransactions() { completedTransactions in
+        SwiftyStoreKit.completeTransactions() { products in
             
-            for completedTransaction in completedTransactions {
+            for product in products {
                 
-                if completedTransaction.transactionState == .purchased || completedTransaction.transactionState == .restored {
+                if product.transaction.transactionState == .purchased || product.transaction.transactionState == .restored {
                     
-                    print("purchased: \(completedTransaction.productId)")
+                    if product.needsFinishTransaction {
+                        // Deliver content from server, then:
+                        SwiftyStoreKit.finishTransaction(product.transaction)
+                    }
+                    print("purchased: \(product.productId)")
                 }
             }
         }

+ 7 - 7
SwiftyStoreKit-iOS-Demo/ViewController.swift

@@ -80,10 +80,10 @@ class ViewController: UIViewController {
         SwiftyStoreKit.purchaseProduct(AppBundleId + "." + purchase.rawValue, atomically: true) { result in
             NetworkActivityIndicatorManager.networkOperationFinished()
             
-            if case .success(_, let transaction) = result {
+            if case .success(let product) = result {
                 // Deliver content from server, then:
-                if let transaction = transaction {
-                    SwiftyStoreKit.finishTransaction(transaction)
+                if product.needsFinishTransaction {
+                    SwiftyStoreKit.finishTransaction(product.transaction)
                 }
             }
             self.showAlert(self.alertForPurchaseResult(result))
@@ -98,8 +98,8 @@ class ViewController: UIViewController {
             
             for product in results.restoredProducts {
                 // Deliver content from server, then:
-                if let transaction = product.transaction {
-                    SwiftyStoreKit.finishTransaction(transaction)
+                if product.needsFinishTransaction {
+                    SwiftyStoreKit.finishTransaction(product.transaction)
                 }
             }
             self.showAlert(self.alertForRestorePurchases(results))
@@ -206,8 +206,8 @@ extension ViewController {
 
     func alertForPurchaseResult(_ result: SwiftyStoreKit.PurchaseResult) -> UIAlertController {
         switch result {
-        case .success(let productId, _):
-            print("Purchase Success: \(productId)")
+        case .success(let product):
+            print("Purchase Success: \(product.productId)")
             return alertWithTitle("Thank You", message: "Purchase completed")
         case .error(let error):
             print("Purchase Failed: \(error)")

+ 8 - 4
SwiftyStoreKit-macOS-Demo/AppDelegate.swift

@@ -35,13 +35,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
     
     func completeIAPTransactions() {
         
-        SwiftyStoreKit.completeTransactions() { completedTransactions in
+        SwiftyStoreKit.completeTransactions() { products in
             
-            for completedTransaction in completedTransactions {
+            for product in products {
                 
-                if completedTransaction.transactionState == .purchased || completedTransaction.transactionState == .restored {
+                if product.transaction.transactionState == .purchased || product.transaction.transactionState == .restored {
                     
-                    print("purchased: \(completedTransaction.productId)")
+                    if product.needsFinishTransaction {
+                        // Deliver content from server, then:
+                        SwiftyStoreKit.finishTransaction(product.transaction)
+                    }
+                    print("purchased: \(product.productId)")
                 }
             }
         }

+ 5 - 5
SwiftyStoreKit-macOS-Demo/ViewController.swift

@@ -77,10 +77,10 @@ class ViewController: NSViewController {
 
         SwiftyStoreKit.purchaseProduct(AppBundleId + "." + purchase.rawValue, atomically: true) { result in
 
-            if case .success(_, let transaction) = result {
+            if case .success(let product) = result {
                 // Deliver content from server, then:
-                if let transaction = transaction {
-                    SwiftyStoreKit.finishTransaction(transaction)
+                if product.needsFinishTransaction {
+                    SwiftyStoreKit.finishTransaction(product.transaction)
                 }
             }
 
@@ -94,8 +94,8 @@ class ViewController: NSViewController {
             
             for product in results.restoredProducts {
                 // Deliver content from server, then:
-                if let transaction = product.transaction {
-                    SwiftyStoreKit.finishTransaction(transaction)
+                if product.needsFinishTransaction {
+                    SwiftyStoreKit.finishTransaction(product.transaction)
                 }
             }
 

+ 8 - 5
SwiftyStoreKit/InAppCompleteTransactionsObserver.swift

@@ -42,20 +42,23 @@ class InAppCompleteTransactionsObserver: NSObject, SKPaymentTransactionObserver
     
     private var callbackCalled: Bool = false
         
-    typealias TransactionsCallback = ([SwiftyStoreKit.CompletedTransaction]) -> ()
+    typealias TransactionsCallback = ([Product]) -> ()
     
     var paymentQueue: SKPaymentQueue {
         return SKPaymentQueue.default()
     }
 
+    let atomically: Bool
+    
     deinit {
         paymentQueue.remove(self)
     }
 
     let callback: TransactionsCallback
     
-    init(callback: @escaping TransactionsCallback) {
+    init(atomically: Bool, callback: @escaping TransactionsCallback) {
     
+        self.atomically = atomically
         self.callback = callback
         super.init()
         paymentQueue.add(self)
@@ -70,7 +73,7 @@ class InAppCompleteTransactionsObserver: NSObject, SKPaymentTransactionObserver
             return
         }
         
-        var completedTransactions: [SwiftyStoreKit.CompletedTransaction] = []
+        var completedTransactions: [Product] = []
         
         for transaction in transactions {
             
@@ -78,9 +81,9 @@ class InAppCompleteTransactionsObserver: NSObject, SKPaymentTransactionObserver
 
             if transactionState != .purchasing {
                 
-                let completedTransaction = SwiftyStoreKit.CompletedTransaction(productId: transaction.payment.productIdentifier, transactionState: transactionState)
+                let product = Product(productId: transaction.payment.productIdentifier, transaction: transaction, needsFinishTransaction: !atomically)
                 
-                completedTransactions.append(completedTransaction)
+                completedTransactions.append(product)
                 
                 print("Finishing transaction for payment \"\(transaction.payment.productIdentifier)\" with state: \(transactionState.stringValue)")
                 

+ 15 - 7
SwiftyStoreKit/InAppProductPurchaseRequest.swift

@@ -25,15 +25,23 @@
 import StoreKit
 import Foundation
 
-public protocol PaymentTransaction { }
+public struct Product {
+    public let productId: String
+    public let transaction: PaymentTransaction
+    public let needsFinishTransaction: Bool
+}
+
+public protocol PaymentTransaction {
+    var transactionState: SKPaymentTransactionState { get }
+}
 
 extension SKPaymentTransaction : PaymentTransaction { }
 
 class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
 
     enum TransactionResult {
-        case purchased(productId: String, transaction: PaymentTransaction?)
-        case restored(productId: String, transaction: PaymentTransaction?)
+        case purchased(product: Product)
+        case restored(product: Product)
         case failed(error: Error)
     }
     
@@ -123,8 +131,8 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
             switch transactionState {
             case .purchased:
                 if isPurchaseRequest {
-                    let pendingTransaction = atomically ? nil : transaction
-                    transactionResults.append(.purchased(productId: transactionProductIdentifier, transaction: pendingTransaction))
+                    let product = Product(productId: transactionProductIdentifier, transaction: transaction, needsFinishTransaction: !atomically)
+                    transactionResults.append(.purchased(product: product))
                     if atomically {
                         paymentQueue.finishTransaction(transaction)
                     }
@@ -139,8 +147,8 @@ class InAppProductPurchaseRequest: NSObject, SKPaymentTransactionObserver {
                 paymentQueue.finishTransaction(transaction)
             case .restored:
                 if !isPurchaseRequest {
-                    let pendingTransaction = atomically ? nil : transaction
-                    transactionResults.append(.restored(productId: transactionProductIdentifier, transaction: pendingTransaction))
+                    let product = Product(productId: transactionProductIdentifier, transaction: transaction, needsFinishTransaction: !atomically)
+                    transactionResults.append(.restored(product: product))
                     if atomically {
                         paymentQueue.finishTransaction(transaction)
                     }

+ 13 - 21
SwiftyStoreKit/SwiftyStoreKit.swift

@@ -72,25 +72,17 @@ public class SwiftyStoreKit {
         case paymentNotAllowed
     }
     public enum PurchaseResult {
-        case success(productId: String, transaction: PaymentTransaction?)
+        case success(product: Product)
         case error(error: PurchaseError)
     }
-    public struct RestoredProduct {
-        public let productId: String
-        public let transaction: PaymentTransaction?
-    }
     public struct RestoreResults {
-        public let restoredProducts: [RestoredProduct]
+        public let restoredProducts: [Product]
         public let restoreFailedProducts: [(Swift.Error, String?)]
     }
     public enum RefreshReceiptResult {
         case success(receiptData: Data)
         case error(error: Error)
     }
-    public struct CompletedTransaction {
-        public let productId: String
-        public let transactionState: SKPaymentTransactionState
-    }
 
     public enum InternalErrorCode: Int {
         case restoredPurchaseWhenPurchasing = 0
@@ -108,8 +100,8 @@ public class SwiftyStoreKit {
         return sharedInstance.inflightPurchases.count > 0 || sharedInstance.restoreRequest != nil
     }
     
-    public class func completeTransactions(_ completion: @escaping ([CompletedTransaction]) -> ()) {
-        sharedInstance.completeTransactionsObserver = InAppCompleteTransactionsObserver(callback: completion)
+    public class func completeTransactions(atomically: Bool = true, completion: @escaping ([Product]) -> ()) {
+        sharedInstance.completeTransactionsObserver = InAppCompleteTransactionsObserver(atomically: atomically, callback: completion)
     }
     
     // MARK: Public methods
@@ -271,26 +263,26 @@ public class SwiftyStoreKit {
 
     private func processPurchaseResult(_ result: InAppProductPurchaseRequest.TransactionResult) -> PurchaseResult {
         switch result {
-        case .purchased(let productId, let transaction):
-            return .success(productId: productId, transaction: transaction)
+        case .purchased(let product):
+            return .success(product: product)
         case .failed(let error):
             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 .restored(let product):
+            return .error(error: .failed(error: storeInternalError(code: InternalErrorCode.restoredPurchaseWhenPurchasing.rawValue, description: "Cannot restore product \(product.productId) from purchase path")))
         }
     }
     
     private func processRestoreResults(_ results: [InAppProductPurchaseRequest.TransactionResult]) -> RestoreResults {
-        var restoredProducts: [RestoredProduct] = []
+        var restoredProducts: [Product] = []
         var restoreFailedProducts: [(Swift.Error, 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 .purchased(let product):
+                restoreFailedProducts.append((storeInternalError(code: InternalErrorCode.purchasedWhenRestoringPurchase.rawValue, description: "Cannot purchase product \(product.productId) from restore purchases path"), product.productId))
             case .failed(let error):
                 restoreFailedProducts.append((error, nil))
-            case .restored(let productId, let transaction):
-                restoredProducts.append(RestoredProduct(productId: productId, transaction: transaction))
+            case .restored(let product):
+                restoredProducts.append(product)
             }
         }
         return RestoreResults(restoredProducts: restoredProducts, restoreFailedProducts: restoreFailedProducts)