StoreKit2|结合 SwiftUI 构建订阅功能界面


在 WWDC23(iOS 17)上,StoreKit 提供了一组 UI 相关的组件,用于更轻松的构建商品营销页面。
- StoreView:用于展示所有商品。适用于销售消耗性商品。
- ProductView:用于展示单个商品。适合用于构建自定义样式的销售页面。
- SubscriptionStoreView:专用于订阅功能的销售。
这些组件基于 SwiftUI 构建,支持使用 SwiftUI 的修饰器,并且也支持跨平台。
StoreView
StoreView
展示一组应用内购买产品,包括其本地化名称、描述和价格,并显示购买按钮。StoreView
只提供最基本的样式,如果要实现更多自定义样式,需要使用 ProductView
和其他组件组合。
基础用法
只需为 StoreView 传递 ids
参数,即可自动加载并显示商品列表。默认显示名称、描述和价格三个信息:


隐藏右上角取消按钮(.storeButton)
默认情况下,StoreView 窗口会自动在右上角添加关闭按钮。
使用 .storeButton
修饰器可以主动隐藏按钮:
.storeButton(.hidden, for: .cancellation)

添加恢复购买按钮(.storeButton)
.storeButton(.visible, for: .restorePurchases)


隐藏描述信息(.productDescription)
使用 .productDescription(.hidden)
修饰器,可以隐藏描述信息的显示:

修改商品样式(.productViewStyle)
.productViewStyle
修饰器可用于 StoreView()
组件或 ProductView()
组件。



添加图标显示
通过 product
闭包设置,可自定义图标组件:

ProductView
StoreView 展示所有商品,ProductView 则用于展示单个商品。
基本用法
ProductView 接受单个 id
参数:
private let productIDs = [
"weisenjoytech.Filmo.subscription.premium.monthly",
"weisenjoytech.Filmo.subscription.premium.yearly",
]
var body: some View {
ScrollView {
VStack(spacing: 16) {
ForEach(productIDs, id: \.self) { id in
ProductView(id: id)
}
}
.padding()
}
}

为每个商品添加背景

.padding()
.background(.background.secondary, in: .buttonBorder)

修改商品样式(.productViewStyle)
.productViewStyle
修饰器可用于 StoreView()
组件或 ProductView()
组件。



SubscriptionStoreView
专用于实现订阅功能。
基本用法
SubscriptionStoreView() 组件接受 Group ID 作为参数。
只需一行代码,即可创建订阅购买页面:


自定义视图 Header(通过闭包)
可以在 SubscriptionStoreView()
组件闭包中传递自定义的 Header(会自动覆盖默认 Header):

修改订阅按钮样式(.subscriptionStoreButtonLabel)
默认情况下,订阅按钮仅显示单行文本:

通过设置 .subscriptionStoreButtonLabel(.multiline)
可以修改它的外观。



推荐优先使用 .multiline
选项。
修改商品列表样式(.subscriptionStoreControlStyle)
.subscriptionStoreControlStyle(.buttons)






设置商品在顶层显示(.subscriptionStoreControlStyle)
当 Header 内容过高时,商品选项会被挤到页面底部,从而被遮挡:

.subscriptionStoreControlStyle
修饰器提供了一个 .placement
参数,设置为 .bottomBar
时,将始终显示在顶层:
.subscriptionStoreControlStyle(.pagedPicker, placement: .bottomBar)

修改商品列表背景(。subscriptionStorePickerItemBackground)
.subscriptionStorePickerItemBackground(.purple.gradient)

添加兑换代码和恢复购买按钮(.storeButton)
.storeButton(.visible, for: .redeemCode)
.storeButton(.visible, for: .restorePurchases)

自定义背景(.containerBackground)

通过指定 .subscriptionStoreFullHeight
,可以为整个页面添加颜色:

由于默认情况下,商品列表和背景之间,会有一个 Material 材质层。所以会看到下面是更浅的颜色。可以在 SubscriptionStoreView
组件上,添加 .backgroundStyle(.clear)
来取消中间层:

还支持使用 View 组件来填充,例如使用 MeshGradient
组件:

为订阅选项分组(.SubscriptionOptionGroup + iOS18+)
适用于有多个订阅群组的场景,可以按照订阅群组进行分组。

还支持为不同的 Group 设置单独的 marketingContent
(顶部的说明信息):

详情可见:

为订阅选项分组设置样式(.subscriptionStoreOptionGroupStyle)
.subscriptionStoreOptionGroupStyle(.links)

如何添加一个买断选项?
SubscriptionStoreView 只能展示订阅选项。如何提供一个买断选项?
我尝试在 SubscriptionStoreView 的 Header 部分,使用 ProductView 添加一个买断选项,但是效果不好:

也许可以通过不使用 ProductView
组件,而是自定义一个买断选项的组件,保持和 SubscriptionStoreView 的订阅选项一样的外观?
更多高级功能
命名与代码组织

参考苹果工程师的方案
- 创建一个 Store 文件夹
- View 组件命名为
SubscriptionOptionView.swift

添加用户退款功能(.refundRequestSheet())
SwiftUI 提供了一个 .refundRequestSheet()
修饰器,可以在 App 内直接弹出申请退款的窗口。


但这个功能并不常用,我并不想在 App 中提供易于使用的退款功能。
添加兑换优惠代码窗口(.offerCodeRedemption())
使用 .offerCodeRedemption(isPresented:onCompletion:)
修饰器:

.presentOfferCodeRedeemSheet(in:)
修饰器。offerCodeRedemption(isPresented:onCompletion:)
方法会显示一个系统表单,供客户输入并兑换订阅优惠码。

- 在生产环境,用户可以输入兑换代码。
- 在本地测试环境,无需输入代码,可以直接选择任意订阅选项。
点击之后,在弹出的支付窗口,可以直接看到 Code 相关信息:

在交易管理器中,也可以看到 Offer 被应用:

当客户兑换优惠码时,StoreKit 会在 updates
中发出结果交易。在应用启动时立即设置交易监听器,以便在应用运行时接收新交易。
当客户兑换有效的优惠代码时,您的应用会在支付队列中收到一笔成功交易,如果您已启用 App Store 服务器通知,还会收到服务器通知。收据中包含一个标识该优惠的 offer_code_ref_name
字段。
添加恢复购买按钮(restorePurchases)
在设置页面添加营销内容
构建自定义的订阅页面
StoreKit 组件的限制
截止 iOS18,StoreKit 提供的系统组件仍存在以下限制:
- SubscriptionStoreView 不支持同时显示订阅和买断选项。
- SubscriptionStoreView 不支持显示营销 Badge,例如下图中这种「早鸟尝鲜」、「-60% 优惠」这样的营销内容。
