永远不会出现确认应用内购买的警报

alert confirming in-app purchase never appears

我有常用的商店工具包队列观察器代码:

func paymentQueue(_ queue: SKPaymentQueue, 
    updatedTransactions transactions: [SKPaymentTransaction]) {
        for t in transactions {
            switch t.transactionState {
            case .purchasing, .deferred: break // do nothing
            case .purchased, .restored:
                let p = t.payment
                if p.productIdentifier == myProductID {
                    // ... set UserDefaults to signify purchase ...
                    // ... put up an alert thanking the user ...
                    queue.finishTransaction(t)
                }
            case .failed:
                queue.finishTransaction(t)
            }
        }
}

问题是在我有评论 "put up an alert thanking the user" 的地方该怎么办。这看起来很简单:我正在创建一个 UIAlertController 并调用 present 来显示它。但有时不出现!

问题似乎与运行时发出自己的警报 ("You're all set") 有关。我没有收到任何通知,所以我不知道这正在发生。我怎样才能确定显示我的 UIAlertController?

问题

您指出了有关应用内购买和 StoreKit 的时间和信息的严重问题。

这里出了什么问题是你(商店观察员)收到 paymentQueue(_:updatedTransactions:) 并且在那一刻 两件事同时发生 ,导致竞争条件:

  • 运行时发出“准备就绪”警报。

  • 您尝试放置您的 UIAlertController(并启动各种其他活动)。

正如您所说的那样,您不会收到 任何 事件来告诉您用户何时关闭了运行时的“一切就绪”警报。那么在警报结束后,你如何做一些事情呢?

此外,如果您尝试在系统发出“您已准备就绪”警报的同时发出警报,您将无声无息地失败 —您的 UIAlertController 警报将永远不会出现。

解决方案

解决方案是识别当系统的“您已准备就绪”警报启动时,您的应用已停用。我们可以检测到这一事实并注册以在您的应用再次激活时收到通知并且是用户关闭“您”的时刻“一切就绪”警报!

因此,现在您可以安全地发出 UIAlertController 警报。

像这样(使用我的 delay 实用程序,参见 ;vc 是我们将在其上显示警报的视图控制器:

let alert = UIAlertController( // ...
// ... configure your alert here ...
delay(0.1) { // important! otherwise there's a race and we can get the wrong answer
    if UIApplication.shared.applicationState == .active {
        vc.present(alert, animated:true)
    } else { // if we were deactivated, present only after we are reactivated
        var ob : NSObjectProtocol? = nil
        ob = NotificationCenter.default.addObserver(
            forName: UIApplication.didBecomeActiveNotification, 
            object: nil, queue: nil) { n in
                NotificationCenter.default.removeObserver(ob as Any)
                delay(0.1) { // can omit this delay, but looks nicer
                    vc.present(alert, animated:true)
                }
            }
    }
}

我已经反复测试过这种方法(虽然很困难,因为测试商店套件的东西效果很差),而且它似乎非常可靠。

仅供参考。我在我的应用程序中实现了类似的行为,但我没有观察到马特在问题中描述的问题。当交易失败时,我的应用程序会显示一个警报来描述失败和建议的操作。下面是我的代码:

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        switch transaction.transactionState {
        ...
        case .failed:
            let (reason, suggestion) = parsePaymentError(error: transaction.error)
            SKPaymentQueue.default().finishTransaction(transaction)
            if let purchaseFailureHandler = self.purchaseFailureHandler {
                DispatchQueue.main.async {
                    purchaseFailureHandler(reason, suggestion)
                }
            }
        }
    }
}

我针对网络连接错误和用户取消错误对代码进行了多次测试。它非常适合我。