Highlight
StoreKit 交易历史 API 终于覆盖已完成的消耗品交易,Transaction 和 RenewalInfo 新增 currency / price 字段,Original API for In-App Purchase 在 iOS 18 被正式标记为 deprecated。
核心内容
开发内购功能时,消耗品购买记录一直是件麻烦事。用户买了 100 金币、消费完毕、交易被 finish 之后,这条记录就从交易历史里消失了。如果你需要展示”购买历史”或做消耗品防重复校验,就得自己建一套数据库来追踪。iOS 18 终于补上了这个缺口:交易历史 API 现在包含已完成的消耗品交易,只需在 Info.plist 里把 SKIncludeConsumableInAppPurchaseHistory 设为 true,框架就会把所有消耗品交易——不管是否已 finish——都纳入查询范围。
另一个长期痛点是价格展示。你能在 App Store Connect 里给产品定价,但 App 内一直拿不到具体的金额和货币信息,只能靠服务端中转或硬编码。iOS 18 给 Transaction 加了 currency 和 price,给 RenewalInfo 加了 currency 和 renewalPrice。这些字段通过 @backDeployed 向下兼容到 iOS 15,老系统上也能用。
UI 层面,去年 WWDC23 引入的 StoreKit Views 今年大幅扩展。新增了 SubscriptionOptionGroup 用来按服务等级分组展示订阅选项,新增 compactPicker / pagedPicker / pagedProminentPicker 三种控制样式,新增 placement API 控制购买按钮的位置,还开放了 SubscriptionStoreControlStyle 协议让你写完全自定义的订阅商店控件。Win-back offers 是今年新增的订阅优惠类型,专门面向流失用户,而且有机会被 Apple 编辑团队在 App Store 的 Today / Games / Apps 标签页推荐展示。
详细内容
已完成消耗品交易进入历史 API(00:40)
此前 Transaction.currentEntitlements 和 Transaction.all 只返回订阅、非消耗品和未完成消耗品的交易。iOS 18 起,在 Info.plist 设置:
<key>SKIncludeConsumableInAppPurchaseHistory</key>
<true/>
之后,正常的 Transaction.updates 监听和 Transaction.all 查询就会包含已完成的消耗品交易,无需额外代码。
Transaction 和 RenewalInfo 新字段(01:32)
Transaction.currency:购买时使用的货币代码(如"USD")Transaction.price:App Store Connect 中配置的价格RenewalInfo.currency:续订时的货币代码RenewalInfo.renewalPrice:下次续订将被收取的金额
这些字段通过 Swift 的 @backDeployed 属性,在 Xcode 16 编译时可用,运行时最低支持 iOS 15。
SubscriptionOptionGroup 与 compactPicker 样式(04:26)
官方给出的 Destination Video 订阅商店示例代码:
import StoreKit
import SwiftUI
struct DestinationVideoShop: View {
var body: some View {
SubscriptionStoreView(groupID: Self.subscriptionGroupID) {
SubscriptionOptionGroupSet { product in
StreamingPassLevel(product)
} label: { streamingPassLevel in
Text(streamingPassLevel.localizedTitle)
} marketingContent: { streamingPassLevel in
StreamingPassMarketingContent(level: streamingPassLevel)
StreamingPassFeatures(level: streamingPassLevel)
}
}
.subscriptionStoreControlStyle(.compactPicker, placement: .bottomBar)
}
}
关键点:
SubscriptionOptionGroupSet根据闭包返回的枚举值(这里是StreamingPassLevel)自动分组——每个唯一值生成一个分组label闭包提供分组标题(如”Premium”/“Basic”),框架自动渲染为 tab 切换marketingContent闭包按当前激活的分组返回不同的营销视图.compactPicker样式把订阅选项压缩为一行横向选择器,placement: .bottomBar把控件固定在底部栏,滚动营销内容时始终可见
分组样式切换(09:06)
除了默认的 .tabs 样式,还可以用 .links 样式:
.subscriptionStoreOptionGroupStyle(.tabs) // tab 切换,适合不同服务等级
.subscriptionStoreOptionGroupStyle(.links) // 导航链接,适合更多选项时用 sheet 展开
自定义控制样式(13:41)
iOS 18 开放了 SubscriptionStoreControlStyle 协议,配合 SubscriptionPicker 和 SubscribeButton 原语,你可以从头构建自己的控件:
import StoreKit
import SwiftUI
struct BadgedPickerControlStyle: SubscriptionStoreControlStyle {
func makeBody(configuration: Configuration) -> some View {
SubscriptionPicker(configuration) { pickerOption in
HStack(alignment: .top) {
VStack(alignment: .leading) {
Text(pickerOption.displayName)
.font(title2.bold())
Text(priceDisplay(for: pickerOption))
if pickerOption.isFamilyShareable {
FamilyShareableBadge()
}
Text(pickerOption.description)
}
Spacer()
SelectionIndicator(pickerOption.isSelected)
}
} confirmation: { option in
SubscribeButton(option)
}
}
}
struct DestinationVideoShop: View {
var body: some View {
SubscriptionStoreView(groupID: Self.subscriptionGroupID) {
SubscriptionPeriodGroupSet { _ in
StreamingPassMarketingContent()
}
}
.subscriptionStoreControlStyle(BadgedPickerControlStyle())
}
}
关键点:
- 遵循
SubscriptionStoreControlStyle协议,只需实现makeBody一个方法 configuration包含构建控件所需的全部数据SubscriptionPicker是框架提供的原语,负责管理选中状态pickerOption.displayName/.isFamilyShareable/.description等属性直接从产品元数据读取SubscribeButton(option)是框架提供的购买按钮原语,点击即触发购买流程SubscriptionPeriodGroupSet是按订阅周期分组的便捷 API
Win-back offers(02:34)
Win-back offers 面向已取消订阅的流失用户。你可以在 App Store Connect 中配置资格规则,无需在 App 里写硬编码判断。展示方式有两种:通过 StoreKit Message API 在 App 内弹出(不需要额外代码),或由 Apple 编辑团队在 App Store 的 Today / Games / Apps 标签页推荐展示。
Xcode 测试增强(15:38)
- StoreKit configuration 新增 App Policies 区域,可本地测试隐私政策和服务条款
- 支持订阅组显示名称的本地化测试
- 可测试内购图片(通过
ProductView的prefersPromotionalIcon) - Dialogs 设置可关闭系统弹窗,方便 UI 自动化测试
- Transaction Manager 支持发送 Purchase Intent,测试 App Store 推广购买
- 支持测试 billing issue 消息,模拟订阅续订失败场景
Original API for In-App Purchase 被废弃(21:06)
iOS 18 起,SKPaymentQueue 等原始 API 被标记为 deprecated,统一收据(unified receipt)同样废弃。已有 App 不受影响可继续运行,但这些 API 不会再获得新功能。StoreKit 2 是唯一会持续更新的路径。
核心启发
-
做什么:用
currency和price字段替代服务端价格查询。为什么值得做:减少一次网络请求,价格展示更实时,用户切换地区时也不用等服务端同步。怎么开始:在展示价格的 SwiftUI 视图中,从Transaction读取currency和price,用NumberFormatter格式化显示;续订价格同理从RenewalInfo.renewalPrice取值。 -
做什么:用
SubscriptionOptionGroupSet重构订阅商店 UI。为什么值得做:用声明式代码替代手写的分组逻辑,tab 切换和营销内容联动由框架处理,代码量大幅减少。怎么开始:定义一个枚举表示服务等级(如 premium / basic),在SubscriptionOptionGroupSet的闭包中返回该枚举值,框架自动按值分组并渲染 tab 或 links 样式。 -
做什么:开启
SKIncludeConsumableInAppPurchaseHistory,删除自建的消耗品购买记录逻辑。为什么值得做:省去维护一套本地数据库的成本,Transaction.all直接返回完整消耗品历史,退款和防重复校验逻辑更简单。怎么开始:在 Info.plist 中添加该 key 并设为true,然后用Transaction.updates监听即可收到已完成消耗品的交易。 -
做什么:为流失用户接入 win-back offers。为什么值得做:Apple 会在 App Store 编辑推荐位展示你的优惠,获取免费流量;资格规则在 App Store Connect 配置,无需发版更新。怎么开始:在 App Store Connect 中创建 win-back offer 并设置资格条件,通过 StoreKit Message API 在 App 内展示,或在服务端通过 App Store Server API 发放。
关联 Session
- Explore App Store server APIs for In-App Purchase — 服务端视角:App Store Server API 新增 transaction 字段与通知类型
- Implement App Store Offers — win-back offers 的完整实现流程与沙盒测试方法
- What’s new in App Store Connect — App Store Connect 新增的推荐提名与营销素材生成功能
评论
GitHub Issues · utterances