Written by: Zhaohe on Thu Aug 14

StoreKit 2 + SubscriptionStoreView

TAGS:
~4 MIN

前言

从iOS17、macOS14开始,苹果使用了新版的StoreKit 2来处理应用内购买和订阅。相比之前的StoreKit,StoreKit 2提供了更简洁和强大的API,简化了开发者的工作流程。

样例

基础使用

StoreKit 2引入了SubscriptionStoreView,这是一个预构建的视图组件,可以轻松地集成到应用中,展示订阅选项和处理购买流程。现在想要展示一个订阅界面,只需要几行代码就能实现。我用 【便便时刻!】 的订阅项做一个演示:

struct PurchaseViewDemo: View {
    let lifetimeProduct = "lifetimeProduct"
    let subscriptionProduct = "subscriptionProduct"

    var body: some View {
        SubscriptionStoreView(productIDs: [lifetimeProduct, subscriptionProduct])
    }
}

可以看到,SubscriptionStoreView只需要传入一个产品ID数组,就能自动处理展示和购买逻辑,非常方便。但是有一个问题,他只能处理订阅和一次性购买的产品,不能处理消耗型和非消耗性产品。比如永久会员这类的,可以看到上图中的 【便便时刻!】 的终身会员是无法通过SubscriptionStoreView来处理的。下期我会用StoreView 来处理这类产品。

修饰符

所有的app内购项目,都必须有一个恢复购买的按钮,还有隐私协议和服务条款的链接。SubscriptionStoreView提供了修饰符来直接显示这几个常见元素:

struct PurchaseViewDemo: View {
    let lifetimeProduct = "lifetimeProduct"
    let subscriptionProduct = "subscriptionProduct"

    var body: some View {
        SubscriptionStoreView(productIDs: [lifetimeProduct, subscriptionProduct])
            .storeButton(.visible, for: .restorePurchases, .policies)
    }
}

使用官方的 .storeButton 修饰符,简简单单就能添加恢复购买和隐私政策按钮。这个修饰符接受一个StoreButtonKind类型的参数,表示按钮的类型。鼠标选中 .restorePurchases 按下 command + click 可以查看官方文档,一共有五种样式。

如果有多个订阅项,那么他默认的样式会发生改变。由于【便便时刻!】只有一个年度会员,所以我用本地的Storekit做演示。这里使用订阅组来展示,可以不用单独一个个写产品ID,他会自动处理订阅组中的所有产品。

struct PurchaseViewDemo: View {
    let lifetimeProduct = "lifetimeProduct"
    let subscriptionProduct = "subscriptionProduct"
    let subscriptionProduct2 = "subscriptionProduct Month"
    let subscriptionGroup = "subscriptionGroup"

    var body: some View {
        // SubscriptionStoreView(productIDs: [subscriptionProduct, subscriptionProduct2]) 同样的效果
        SubscriptionStoreView(groupID: subscriptionGroup)
            .storeButton(.visible, for: .restorePurchases, .policies)
            // 控制订阅按钮的样式
            .subscriptionStoreButtonLabel(.multiline)
    }
}

可以看到,多个订阅项出现时,SubscriptionStoreView 会自动处理订阅组的展示。你只需要传入订阅组ID或者多个产品ID,它就会帮你处理好。并且和单独一个订阅时的模式样式不同,会显示简介和价格等信息。使用 .subscriptionStoreButtonLabel(.multiline) 修饰符,可以将订阅按钮的标签设置为多行显示,还有 .singleLine 等样式可选。

购买

现在想要使用SubscriptionStoreView来处理购买流程,也很简单,同样提供了修饰符来处理。

struct PurchaseViewDemo: View {
    let lifetimeProduct = "lifetimeProduct"
    let subscriptionProduct = "subscriptionProduct"
    let subscriptionProduct2 = "subscriptionProduct Month"
    let subscriptionGroup = "subscriptionGroup"

    var body: some View {
        // SubscriptionStoreView(productIDs: [subscriptionProduct, subscriptionProduct2]) 同样的效果
        SubscriptionStoreView(groupID: subscriptionGroup)
            .storeButton(.visible, for: .restorePurchases, .policies)
            // 控制订阅按钮的样式
            .subscriptionStoreButtonLabel(.multiline)
            .onInAppPurchaseStart { product in
                print("onInAppPurchaseStart 执行 \(product.displayName)")
            }
            .onInAppPurchaseCompletion { product, result in
                print("onInAppPurchaseCompletion 执行 \(product.displayName)")
                switch result {
                case .success(.pending):
                    print("购买被挂起")
                case .success(.userCancelled):
                    print("用户取消购买")
                case .success(.success(let res)):
                    print(res)
                    print("购买成功 ")
                case .success:
                    print("success")
                case .failure:
                    print("failure")
                }
            }
    }
}

当选中一个订阅项并按下时,触发 .onInAppPurchaseStart 按下可以看到控制打印 onInAppPurchaseStart 执行 月会员。当购买完成后,触发 .onInAppPurchaseCompletion 并执行代码,可以根据结果来判断购买是否成功。

    onInAppPurchaseStart 执行 月会员
    onInAppPurchaseCompletion 执行 月会员
    用户取消购买
    onInAppPurchaseStart 执行 月会员
    onInAppPurchaseCompletion 执行 月会员
    {
    "header" : {...},
    "payload" : {...},
    "signature" : "64 bytes (verified)"
    }
    购买成功 

case .success(.success(let res)): 这个结果是购买成功后的详细信息,可以根据需要进行处理。比如再次验证是否有效、获取购买项详细信息、更新UI等。这里只是展示一下,使用新版的StoreKit 2来处理购买流程是多么简单。