如何用 StoreKit 2 恢复?

How restore with StoreKit 2?

iOS 15 介绍了 StoreKit 2。我正在查看它是否可以在我现有的应用程序中采用它,但我不知道该怎么做。特别是,我看不到如何实现所需的恢复功能(例如,如果用户删除了我的应用程序)。

我想我们应该使用 Transaction.latest(for:)?是吗?

但在我的测试中,如果用户使用 StoreKit 1 完成购买,则调用 returns nil。这是真的?或者,如果我做错了,从 StoreKit 1 迁移到 StoreKit 2 的正确方法是什么,我们如何处理恢复?

您可以遍历 Transaction.currentEntitlements 以获得所有有效订阅和之前购买的 non-consumable 产品。

如果您在启动时选中此 collection,您可以在没有用户交互的情况下静默恢复任何以前的购买。

为了扩展 Paulw11 的正确答案,我将只演示我最终得到的实际代码。

StoreKit 2 真的很简单。与商店或与商店信息交互主要只是打开连续信封的问题。我只有一个应用内购买,而且它是非消耗品,所以当用户要求购买时,我就是这样做的:

if let purchase = try? await product.purchase() {
    if case let .success(result) = purchase {
        if case let .verified(trans) = result {
            if trans.productID == IAPUtilities.productid {
                IAPUtilities.signalPurchaseSuccess()
                await trans.finish()
                return
            }
        }
    }
}
// but if we get here, we must have failed
IAPUtilities.signalPurchaseFailure()

同样,当用户要求恢复购买时,我是这样处理的:

for await result in Transaction.currentEntitlements {
    if case let .verified(trans) = result {
        if trans.productID == IAPUtilities.productid {
            IAPUtilities.signalPurchaseSuccess()
            withUnsafeCurrentTask { task in
                task?.cancel()
            }
            await trans.finish()
            return
        }
    }
}
// but if we get here, we must have failed
IAPUtilities.signalRestorationFailure()

根据我的测试,权利信息似乎以某种方式存储在磁盘上。因此,如果这不是应用程序的新安装,则恢复能够在不进行任何联网的情况下确认购买。但如果新安装的应用,授权信息是通过网络与商店对话获得的。看来你不需要在这种交易上调用 finish,但我还是这样做了,因为它似乎没有害处。

在 Apple 的 SKDemo 中,他们 this

Button("Restore Purchases", action: {
            async {
                //This call displays a system prompt that asks users to authenticate with their App Store credentials.
                //Call this function only in response to an explicit user action, such as tapping a button.
                try? await AppStore.sync()
            }
        })

我个人想知道成功与否,所以用一个方法包裹起来

func restore() async -> Bool {
    return ((try? await AppStore.sync()) != nil)
}

并相应地弹出警报