Forráskód Böngészése

Retrieve all products infos at once

Andrea Bizzotto 9 éve
szülő
commit
d1139480bc

+ 13 - 10
SwiftyStoreDemo/ViewController.swift

@@ -47,7 +47,7 @@ class ViewController: UIViewController {
     func getInfo(no: String) {
         
         NetworkActivityIndicatorManager.networkOperationStarted()
-        SwiftyStoreKit.retrieveProductInfo(AppBundleId + ".purchase" + no) { result in
+        SwiftyStoreKit.retrieveProductsInfo([AppBundleId + ".purchase" + no]) { result in
             NetworkActivityIndicatorManager.networkOperationFinished()
             
             self.showAlert(self.alertForProductRetrievalInfo(result))
@@ -119,14 +119,18 @@ extension ViewController {
         }
     }
 
-    func alertForProductRetrievalInfo(result: SwiftyStoreKit.RetrieveResult) -> UIAlertController {
+    func alertForProductRetrievalInfo(result: RetrieveResult) -> UIAlertController {
         
-        switch result {
-        case .Success(let product):
+        if let product = result.retrievedProducts.first {
             let priceString = NSNumberFormatter.localizedStringFromNumber(product.price, numberStyle: .CurrencyStyle)
             return alertWithTitle(product.localizedTitle, message: "\(product.localizedDescription) - \(priceString)")
-        case .Error(let error):
-            return alertWithTitle("Could not retrieve product info", message: String(error))
+        }
+        else if let invalidProductId = result.invalidProductIDs.first {
+            return alertWithTitle("Could not retrieve product info", message: "Invalid product identifier: \(invalidProductId)")
+        }
+        else {
+            let errorString = result.error?.localizedDescription ?? "Unknown error. Please contact support"
+            return alertWithTitle("Could not retrieve product info", message: errorString)
         }
     }
 
@@ -139,13 +143,12 @@ extension ViewController {
             print("Purchase Failed: \(error)")
             switch error {
                 case .Failed(let error):
-                    if case ResponseError.RequestFailed(let internalError) = error where internalError.domain == SKErrorDomain {
-                        return alertWithTitle("Purchase failed", message: "Please check your Internet connection or try again later")
-                    }
-                    if (error as NSError).domain == SKErrorDomain {
+                    if error.domain == SKErrorDomain {
                         return alertWithTitle("Purchase failed", message: "Please check your Internet connection or try again later")
                     }
                     return alertWithTitle("Purchase failed", message: "Unknown error. Please contact support")
+                case .InvalidProductId(let productId):
+                    return alertWithTitle("Purchase failed", message: "\(productId) is not a valid product identifier")
                 case .NoProductIdentifier:
                     return alertWithTitle("Purchase failed", message: "Product not found")
                 case .PaymentNotAllowed:

+ 14 - 26
SwiftyStoreKit/InAppProductQueryRequest.swift

@@ -24,19 +24,15 @@
 
 import StoreKit
 
-public enum ResponseError : ErrorType {
-    case InvalidProducts(invalidProductIdentifiers: [String])
-    case NoProducts
-    case RequestFailed(error: NSError)
+public struct RetrieveResult {
+    public let retrievedProducts: Set<SKProduct>
+    public let invalidProductIDs: Set<String>
+    public let error: NSError?
 }
-class InAppProductQueryRequest: NSObject, SKProductsRequestDelegate {
 
-    enum ResultType {
-        case Success(products: [SKProduct])
-        case Error(e: ResponseError)
-    }
+class InAppProductQueryRequest: NSObject, SKProductsRequestDelegate {
 
-    typealias RequestCallback = (result: ResultType) -> ()
+    typealias RequestCallback = (result: RetrieveResult) -> ()
     private let callback: RequestCallback
     private let request: SKProductsRequest
     // http://stackoverflow.com/questions/24011575/what-is-the-difference-between-a-weak-reference-and-an-unowned-reference
@@ -71,21 +67,13 @@ class InAppProductQueryRequest: NSObject, SKProductsRequestDelegate {
     // MARK: SKProductsRequestDelegate
     func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
         
-        if let invalidProductIdentifiers = response._invalidProductIdentifiers where invalidProductIdentifiers.count > 0 {
-            let error = ResponseError.InvalidProducts(invalidProductIdentifiers: invalidProductIdentifiers)
-            dispatch_async(dispatch_get_main_queue()) {
-                self.callback(result: .Error(e: error))
-            }
-            return
-        }
-        guard let products = response._products where products.count > 0 else {
-            let error = ResponseError.NoProducts
-            dispatch_async(dispatch_get_main_queue()) {
-                self.callback(result: .Error(e: error))
-            }
-            return
+        dispatch_async(dispatch_get_main_queue()) {
+            
+            let retrievedProducts = Set<SKProduct>(response.products ?? [])
+            let invalidProductIDs = Set<String>(response.invalidProductIdentifiers ?? [])
+            self.callback(result: RetrieveResult(retrievedProducts: retrievedProducts,
+                invalidProductIDs: invalidProductIDs, error: nil))
         }
-        callback(result: .Success(products: products))
     }
     
     func requestDidFinish(request: SKRequest) {
@@ -107,9 +95,9 @@ class InAppProductQueryRequest: NSObject, SKProductsRequestDelegate {
     }
     #endif
     func requestFailed(error: NSError){
-        let error = ResponseError.RequestFailed(error: error)
         dispatch_async(dispatch_get_main_queue()) {
-            self.callback(result: .Error(e: error))
+            self.callback(result: RetrieveResult(retrievedProducts: [],
+                invalidProductIDs: [], error: error))
         }
     }
 }

+ 30 - 43
SwiftyStoreKit/SwiftyStoreKit.swift

@@ -34,11 +34,21 @@ public class SwiftyStoreKit {
                 products[productIdentifier] = product
             }
         }
+        func allProductsMatching(productIds: Set<String>) -> Set<SKProduct>? {
+            var requestedProducts = Set<SKProduct>()
+            for productId in productIds {
+                guard let product = products[productId] else {
+                    return nil
+                }
+                requestedProducts.insert(product)
+            }
+            return requestedProducts
+        }
     }
     private var store: InAppPurchaseStore = InAppPurchaseStore()
 
     // As we can have multiple inflight queries and purchases, we store them in a dictionary by product id
-    private var inflightQueries: [String: InAppProductQueryRequest] = [:]
+    private var inflightQueries: [Set<String>: InAppProductQueryRequest] = [:]
     private var inflightPurchases: [String: InAppProductPurchaseRequest] = [:]
     private var restoreRequest: InAppProductPurchaseRequest?
     #if os(iOS)
@@ -46,7 +56,8 @@ public class SwiftyStoreKit {
     #endif
     // MARK: Enums
     public enum PurchaseError {
-        case Failed(error: ErrorType)
+        case Failed(error: NSError)
+        case InvalidProductId(productId: String)
         case NoProductIdentifier
         case PaymentNotAllowed
     }
@@ -54,10 +65,6 @@ public class SwiftyStoreKit {
         case Success(productId: String)
         case Error(error: PurchaseError)
     }
-    public enum RetrieveResult {
-        case Success(product: SKProduct)
-        case Error(error: ErrorType)
-    }
     public struct RestoreResults {
         public let restoredProductIds: [String]
         public let restoreFailedProducts: [(ErrorType, String?)]
@@ -79,21 +86,14 @@ public class SwiftyStoreKit {
     }
     
     // MARK: Public methods
-    public class func retrieveProductInfo(productId: String, completion: (result: RetrieveResult) -> ()) {
-        guard let product = sharedInstance.store.products[productId] else {
+    public class func retrieveProductsInfo(productIds: Set<String>, completion: (result: RetrieveResult) -> ()) {
+        
+        guard let products = sharedInstance.store.allProductsMatching(productIds) else {
             
-            sharedInstance.requestProduct(productId) { (inner: () throws -> SKProduct) -> () in
-                do {
-                    let product = try inner()
-                    completion(result: .Success(product: product))
-                }
-                catch let error {
-                    completion(result: .Error(error: error))
-                }
-            }
+            sharedInstance.requestProducts(productIds, completion: completion)
             return
         }
-        completion(result: .Success(product: product))
+        completion(result: RetrieveResult(retrievedProducts: products, invalidProductIDs: [], error: nil))
     }
     
     public class func purchaseProduct(productId: String, completion: (result: PurchaseResult) -> ()) {
@@ -102,20 +102,22 @@ public class SwiftyStoreKit {
             sharedInstance.purchase(product: product, completion: completion)
         }
         else {
-            retrieveProductInfo(productId) { (result) -> () in
-                if case .Success(let product) = result {
+            retrieveProductsInfo(Set([productId])) { result -> () in
+                if let product = result.retrievedProducts.first {
                     sharedInstance.purchase(product: product, completion: completion)
                 }
-                else if case .Error(let error) = result {
+                else if let error = result.error {
                     completion(result: .Error(error: .Failed(error: error)))
                 }
+                else if let invalidProductId = result.invalidProductIDs.first {
+                    completion(result: .Error(error: .InvalidProductId(productId: invalidProductId)))
+                }
             }
         }
     }
     
     public class func restorePurchases(completion: (results: RestoreResults) -> ()) {
 
-        // Called multiple
         sharedInstance.restoreRequest = InAppProductPurchaseRequest.restorePurchases() { results in
         
             sharedInstance.restoreRequest = nil
@@ -215,30 +217,15 @@ public class SwiftyStoreKit {
         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)) -> ()) -> () {
+    private func requestProducts(productIds: Set<String>, completion: (result: RetrieveResult) -> ()) {
         
-        inflightQueries[productId] = InAppProductQueryRequest.startQuery([productId]) { result in
+        inflightQueries[productIds] = InAppProductQueryRequest.startQuery(productIds) { result in
         
-            self.inflightQueries[productId] = nil
-            if case .Success(let products) = result {
-                
-                // Add to Store
-                for product in products {
-                    //print("Received product with ID: \(product.productIdentifier)")
-                    self.store.addProduct(product)
-                }
-                guard let product = self.store.products[productId] else {
-                    completion(result: { throw ResponseError.NoProducts })
-                    return
-                }
-                completion(result: { return product })
-            }
-            else if case .Error(let error) = result {
-                
-                completion(result: { throw error })
+            self.inflightQueries[productIds] = nil
+            for product in result.retrievedProducts {
+                self.store.addProduct(product)
             }
+            completion(result: result)
         }
     }
     

+ 13 - 10
SwiftyStoreOSXDemo/ViewController.swift

@@ -45,7 +45,7 @@ class ViewController: NSViewController {
     }
     func getInfo(no: String) {
 
-        SwiftyStoreKit.retrieveProductInfo(AppBundleId + ".purchase" + no) { result in
+        SwiftyStoreKit.retrieveProductsInfo([AppBundleId + ".purchase" + no]) { result in
 
             self.showAlert(self.alertForProductRetrievalInfo(result))
         }
@@ -104,14 +104,18 @@ extension ViewController {
         }
     }
 
-    func alertForProductRetrievalInfo(result: SwiftyStoreKit.RetrieveResult) -> NSAlert {
+    func alertForProductRetrievalInfo(result: RetrieveResult) -> NSAlert {
         
-        switch result {
-        case .Success(let product):
+        if let product = result.retrievedProducts.first {
             let priceString = NSNumberFormatter.localizedStringFromNumber(product.price ?? 0, numberStyle: .CurrencyStyle)
             return alertWithTitle(product.localizedTitle ?? "no title", message: "\(product.localizedDescription) - \(priceString)")
-        case .Error(let error):
-            return alertWithTitle("Could not retrieve product info", message: String(error))
+        }
+        else if let invalidProductId = result.invalidProductIDs.first {
+            return alertWithTitle("Could not retrieve product info", message: "Invalid product identifier: \(invalidProductId)")
+        }
+        else {
+            let errorString = result.error?.localizedDescription ?? "Unknown error. Please contact support"
+            return alertWithTitle("Could not retrieve product info", message: errorString)
         }
     }
     
@@ -125,13 +129,12 @@ extension ViewController {
             print("Purchase Failed: \(error)")
             switch error {
             case .Failed(let error):
-                if case ResponseError.RequestFailed(let internalError) = error where internalError.domain == SKErrorDomain {
-                    return alertWithTitle("Purchase failed", message: "Please check your Internet connection or try again later")
-                }
-                if (error as NSError).domain == SKErrorDomain {
+                if error.domain == SKErrorDomain {
                     return alertWithTitle("Purchase failed", message: "Please check your Internet connection or try again later")
                 }
                 return alertWithTitle("Purchase failed", message: "Unknown error. Please contact support")
+            case .InvalidProductId(let productId):
+                return alertWithTitle("Purchase failed", message: "\(productId) is not a valid product identifier")
             case .NoProductIdentifier:
                 return alertWithTitle("Purchase failed", message: "Product not found")
             case .PaymentNotAllowed: