代码之家  ›  专栏  ›  技术社区  ›  laurie

在等待应用商店响应应用内购买时,如何运行“加载微调器”或类似程序?

  •  0
  • laurie  · 技术社区  · 8 年前

    我目前在基于Swift的iOS应用程序中提供应用内购买。我有一个Store类,它负责重载,还有一个StoreViewController,它管理商店的视图。目前,当你按下购买按钮时,商店需要很长时间才能做出响应(至少10秒以上)。这意味着你可以反复按下按钮或放弃购买。我想更改它,这样当你按下一个按钮时,它会禁用按钮并显示一个微调器或类似内容,直到购买过程准备好继续。在我已经掌握的代码中,首选的方法/位置是什么?

    门店类别:

    import Foundation
    import StoreKit
    
    protocol ClassStoreDelegate: class {
        func storeUpdateReceived(store: Store)
    }
    
    class Store: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
        // Properties
        weak var delegate: ClassStoreDelegate?
        var list = [SKProduct]()
        var p = SKProduct()
        var localPrice: String?
        var presentAlert = false
        var alertTitle = "Alert"
        var alertMessage = "Unfortunately something went wrong"
        var purchaseSuccess = false
    
        // Methods
        // Calling the delegate method
        func storeUpdate() {
            delegate?.storeUpdateReceived(store: self)
        }
    
        // Buy the product
        func buy(productId: String) {
            var foundProduct = false
            var counter = 0
            while counter < list.count && !foundProduct {
                if (list[counter].productIdentifier == productId){
                    p = list[counter]
                    foundProduct = true
                }
                counter = counter + 1
            }
            buyProduct()
        }
    
        func buyProduct() {
            let pay = SKPayment(product: p)
            SKPaymentQueue.default().add(self)
            SKPaymentQueue.default().add(pay as SKPayment)
        }
    
        func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            for transaction: AnyObject in transactions {
                let trans = transaction as! SKPaymentTransaction
                switch trans.transactionState {
                case .purchased:
                    let prodID = p.productIdentifier
                    switch prodID {
                    case "volume2":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 1)
                        purchaseSuccess = true
                        delegate?.storeUpdateReceived(store: self)
                    case "volume3":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 2)
                        purchaseSuccess = true
                        delegate?.storeUpdateReceived(store: self)
                    case "volume4":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 3)
                        purchaseSuccess = true
                        delegate?.storeUpdateReceived(store: self)
                    case "volume5":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 4)
                        purchaseSuccess = true
                        delegate?.storeUpdateReceived(store: self)
                    case "all":
                        for i in 0...VolumeTableViewController.unlocked.count-1 {
                            VolumeTableViewController.saveUnlocked(volumeUnlocked: i)
                        }
                        purchaseSuccess = true
                        delegate?.storeUpdateReceived(store: self)
                    default:
                        delegate?.storeUpdateReceived(store: self)
                    }
                    queue.finishTransaction(trans)
                    break
                case .failed:
                    alertTitle = "Something went wrong"
                    alertMessage = "Unfortunately the purchase failed"
                    presentAlert = true
                    delegate?.storeUpdateReceived(store: self)
                    queue.finishTransaction(trans)
                    break
                case .restored:
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
                    // Try to pop back after successful restore
                    purchaseSuccess = true
                    delegate?.storeUpdateReceived(store: self)
                    queue.finishTransaction(trans)
                    break
                default:
                    break
                }
            }
        }
        // Restore products
        func restore() {
            SKPaymentQueue.default().add(self)
            SKPaymentQueue.default().restoreCompletedTransactions()
        }
    
        func getProducts() {
            if(SKPaymentQueue.canMakePayments()) {
                let productID: NSSet = NSSet(objects: "volume2", "volume3", "volume4", "volume5", "all")
                let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
                request.delegate = self
                request.start()
            } else {
                alertTitle = "Something went wrong"
                alertMessage = "Your phone does not appear to be set up for making purchases"
                presentAlert = true
                delegate?.storeUpdateReceived(store: self)
            }
        }
    
        func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
            let myProduct = response.products
            for product in myProduct {
                list.append(product)
            }
            delegate?.storeUpdateReceived(store: self)
        }
    
        func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
            let transactionsArray = queue.transactions
            if (transactionsArray.isEmpty) {
                alertTitle = "Something went wrong"
                alertMessage = "We weren't able to find any previous purchases for your account"
                presentAlert = true
                delegate?.storeUpdateReceived(store: self)
            }
            else {
                for transaction in transactionsArray {
                    let t: SKPaymentTransaction = transaction
                    let prodID = t.payment.productIdentifier as String
                    switch prodID {
                    case "volume2":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 1)
                        delegate?.storeUpdateReceived(store: self)
                    case "volume3":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 2)
                        delegate?.storeUpdateReceived(store: self)
                    case "volume4":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 3)
                        delegate?.storeUpdateReceived(store: self)
                    case "volume5":
                        VolumeTableViewController.saveUnlocked(volumeUnlocked: 4)
                        delegate?.storeUpdateReceived(store: self)
                    case "all":
                        for i in 0...VolumeTableViewController.unlocked.count-1 {
                            VolumeTableViewController.saveUnlocked(volumeUnlocked: i)
                        }
                        delegate?.storeUpdateReceived(store: self)
                    default:
                        alertTitle = "Something went wrong"
                        alertMessage = "We weren't able to find the correct product"
                        presentAlert = true
                        delegate?.storeUpdateReceived(store: self)
                    }
                }
            }
        }
        // Format the price and display
        func formatPrice(price: NSDecimalNumber) -> String {
            let formatter = NumberFormatter()
            formatter.locale = Locale.current
            formatter.numberStyle = .currency
            if let formattedPrice = formatter.string(from: price){
                localPrice = (" \(formattedPrice)")
            }
            return localPrice!
        }
    }
    

    StoreViewController类:

    let store = Store() // Global store instance
    
    import UIKit
    
    class StoreViewController: UIViewController, ClassStoreDelegate {
        // Properties
        /*  let alert = UIAlertController(title: "Something went wrong", message:
         "Unfortunately, something went wrong with your request", preferredStyle: UIAlertControllerStyle.alert) */
        // Actions
        @IBAction func btn2(_ sender: UIButton) {
            store.buy(productId: store.list[0].productIdentifier)
        }
    
        @IBAction func btn3(_ sender: UIButton) {
            store.buy(productId: store.list[1].productIdentifier)
        }
    
        @IBAction func btn4(_ sender: UIButton) {
            store.buy(productId: store.list[2].productIdentifier)
        }
    
        @IBAction func btn5(_ sender: UIButton) {
            store.buy(productId: store.list[3].productIdentifier)
        }
    
        @IBAction func btnAll(_ sender: UIButton) {
            store.buy(productId: store.list[4].productIdentifier)
        }
    
        @IBAction func btnRestore(_ sender: UIButton) {
            store.restore()
        }
        // Outlets
        @IBOutlet weak var btn2: UIButton!
        @IBOutlet weak var btn3: UIButton!
        @IBOutlet weak var btn4: UIButton!
        @IBOutlet weak var btn5: UIButton!
        @IBOutlet weak var btnAll: UIButton!
        @IBOutlet weak var btnRestore: UIButton!
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Disable buttons until prices loaded
            btn2.isEnabled = false
            btn3.isEnabled = false
            btn4.isEnabled = false
            btn5.isEnabled = false
            btnAll.isEnabled = false
            btnRestore.isEnabled = false
    
            store.delegate = self // bind the delegate like this?
            //      alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default)) // Set up the action for the alert button
    
            // Get list of products for the store
            self.navigationItem.title = "Store"
            // update once the list of products is got from the store object
            store.getProducts()
        }
    
        // Running the delegate update
        func storeUpdateReceived(store: Store) {
            if ((store.list.count) > 0) {
                            btnRestore.setTitle("Restore", for: .normal)
    
                if store.list[0].productIdentifier == "all" {
                                    btn2.setTitle(store.list[0].localizedTitle + " " + store.formatPrice(price: store.list[0].price), for: .normal)
                }
                if store.list[1].productIdentifier == "volume2" {
                    if VolumeTableViewController.unlocked[1] {
                        btn3.isHidden = true
                    } else {
                        btn3.setTitle(store.list[1].localizedTitle + " " + store.formatPrice(price: store.list[1].price), for: .normal)
                    }
                }
                            if store.list[2].productIdentifier == "volume3" {
                    if VolumeTableViewController.unlocked[2] {
                        btn4.isHidden = true
                    } else {
                        btn4.setTitle(store.list[2].localizedTitle + " " + store.formatPrice(price: store.list[2].price), for: .normal)
                    }
                }
                if store.list[3].productIdentifier  == "volume4" {
                    if VolumeTableViewController.unlocked[3] {
                        btn5.isHidden = true
                    } else {
                        btn5.setTitle(store.list[3].localizedTitle + " " + store.formatPrice(price: store.list[3].price), for: .normal)
                    }
                }
                if store.list[4].productIdentifier == "volume5" {
                    if VolumeTableViewController.unlocked[4] {
                        btnAll.isHidden = true
                    } else {
                        btnAll.setTitle(store.list[4].localizedTitle + " " + store.formatPrice(price: store.list[4].price), for: .normal)
                    }
                }
    
                // Now enable the buttons
                btn2.isEnabled = true
                btn3.isEnabled = true
                btn4.isEnabled = true
                btn5.isEnabled = true
                btnAll.isEnabled = true
                btnRestore.isEnabled = true
            }
            if store.purchaseSuccess {
            performSegueToReturnBack()
                store.purchaseSuccess = false
            }
        }
    
        // method to go back when complete
        func performSegueToReturnBack()  {
            if let nav = self.navigationController {
                nav.popViewController(animated: true)
            } else {
                self.dismiss(animated: true, completion: nil)
            }
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    }
    
    2 回复  |  直到 8 年前
        1
  •  4
  •   Skaal    5 年前

    实际上,您可以使用 SKPaymentTransactionObserver 检查交易状态的协议。

    这个 .purchasing 案例 updatedTransactions 委托告诉您事务何时开始运行:

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .purchasing:
                // Show your loader - Disable interactions here
            }
        }
    }
    

    这个 removedTransactions 委托告诉您何时从队列中删除事务:

    func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {
        // Hide your loader - Enable interactions here
    }
    

    如果您有多个交易,您还可以检查您的 transactions 数组是否为空:

        if transactions.isEmpty {
            // Hide your loader - Enable interactions here
        }
    
        2
  •  1
  •   matt    8 年前

    我想更改它,这样当你按下一个按钮时,它就会禁用按钮

    然后禁用按钮或隐藏购买界面,以立即响应点击购买按钮。

    并显示微调器或类似内容,直到购买过程准备好继续

    你不能。你没有办法知道发生了什么。“购买过程”是应用程序外部的用户和运行时之间的交换。你不知道发生了什么事。如果用户取消购买,或者用户的密码或商店帐户有任何问题,您将不会收到任何事件通知您发生了什么。因此,如果你设置一些特殊的“等待”界面,它可能会永远存在。

    基本上,你问题的标题揭示了一种深刻的误解。你知道 “等等”。你只要继续你的应用程序的生活。采购流程是 异步 确实如此 进程外 适用于你的应用程序。如果付款队列告诉你的应用程序发生了什么事情,你会做出回应;这就是全部。

    (参见 Apple's guidance about this ; 正如他们所指出的那样,在某些情况下,来自支付队列的消息可能需要几天才能到达!“等着”你会显得很傻。)

    所以正确的程序是我们首先说的。如果用户点击“购买”,则禁用或关闭购买界面,仅此而已。