好的,我们来详细了解一下 SubscriptionStoreView
,这是苹果在 StoreKit 中引入的一个强大的 SwiftUI 组件,旨在极大简化应用内订阅购买界面的构建。
SubscriptionStoreView
是什么?
SubscriptionStoreView
是一个预构建的 SwiftUI 视图,它可以自动加载并展示你在 App Store Connect 中配置的自动续订订阅产品。它负责处理产品的显示、价格的本地化、购买流程的发起,甚至包括一些常见的 UI 元素,如购买按钮、服务条款链接等。
核心优势与使用场景
- 快速实现订阅购买界面: 对于开发者来说,最大的好处是无需从头手动构建复杂的订阅展示 UI。
SubscriptionStoreView
提供了多种标准化的样式,可以快速搭建出符合苹果设计规范的购买页面。 - 自动适配与本地化: 该视图会自动拉取 App Store Connect 中配置的产品信息(名称、描述、价格),并根据用户设备的地区设置进行本地化显示。
- 标准化用户体验: 使用
SubscriptionStoreView
可以为用户提供一个熟悉且一致的订阅购买体验,减少用户的学习成本。 - 处理复杂逻辑: 它内部处理了产品加载、价格显示、促销优惠(如果配置了)、家庭共享信息以及购买流程的调用。
- 减少维护成本: 由于 UI 和部分逻辑由苹果提供和维护,可以减少开发者因 StoreKit API 变更或 UI 规范更新带来的维护工作。
主要使用场景:
- 应用主付费墙 (Paywall): 当用户首次遇到需要订阅才能访问的功能时,展示
SubscriptionStoreView
来呈现所有可用的订阅选项。 - 应用内升级/追加销售页面: 对于已在使用免费版或低级别订阅的用户,可以使用此视图展示更高级别的订阅选项。
- 用户引导流程 (Onboarding): 在新用户引导流程的最后,展示订阅选项,鼓励用户订阅。
- 设置或账户页面: 在应用的设置或用户账户区域提供一个入口,让用户可以方便地查看和管理(或发起新的)订阅。
可用性
SubscriptionStoreView
是在 iOS 17, iPadOS 17, macOS 14 (Sonoma), watchOS 10, tvOS 17, visionOS 1 及更高版本中引入的。因此,如果你的应用需要支持更早的操作系统版本,则无法直接使用它,需要自行构建 UI 或使用旧的 StoreKit API。
如何使用 SubscriptionStoreView
使用 SubscriptionStoreView
非常直接。你主要需要提供一个订阅组 ID (Subscription Group ID),这是你在 App Store Connect 中为一组订阅产品定义的标识符。
Swift
import SwiftUI
import StoreKit
struct MySubscriptionPaywallView: View {
// 替换成你在 App Store Connect 中配置的订阅组 ID
let subscriptionGroupID = "YOUR_SUBSCRIPTION_GROUP_ID"
// 可选:如果你想优先展示或只展示特定的产品ID
// let productIDsToShow: [String] = ["com.example.monthly", "com.example.yearly"]
@Environment(\.dismiss) var dismiss
var body: some View {
// 基本用法:提供订阅组 ID
SubscriptionStoreView(groupID: subscriptionGroupID) {
// 在这里可以放置你的 App Icon, 营销文本等作为视图的背景或前景内容
// 这部分内容会与 StoreKit 渲染的订阅选项结合显示
VStack {
Image("MyAppIcon") // 示例
.resizable()
.frame(width: 80, height: 80)
.clipShape(RoundedRectangle(cornerRadius: 16))
Text("Unlock All Features!")
.font(.largeTitle.bold())
Text("Subscribe now to get access to exclusive content and features.")
.font(.subheadline)
.multilineTextAlignment(.center)
.padding(.horizontal)
}
.padding(.vertical)
}
// 可选:只展示特定的产品 (如果产品ID列表不为空)
// .products(productIDsToShow.isEmpty ? nil : productIDsToShow.map { Product.ID($0) })
// ----- 自定义修饰符 -----
// 1. 自定义购买按钮标签
.subscriptionStoreButtonLabel(.multiline) // 或者 .automatic, .singleLine
// 2. 自定义订阅选项的背景 (例如,让每个选项卡片化)
.subscriptionStorePickerItemBackground(.thinMaterial) // 或者 .regularMaterial, .thickMaterial, .ultraThinMaterial, .ultraThickMaterial, Color.gray.opacity(0.1)
// 3. 控制订阅选项的样式
.subscriptionStoreControlStyle(.automatic) // 或者 .picker, .prominentPicker (iOS 17.2+)
// 4. 提供服务条款和隐私政策链接 (强烈推荐)
.subscriptionStorePolicyDestination(for: .privacyPolicy) {
// 跳转到你的隐私政策视图或 URL
MyPrivacyPolicyView()
}
.subscriptionStorePolicyDestination(for: .termsOfService) {
// 跳转到你的服务条款视图或 URL
MyTermsOfServiceView()
}
// 也可以直接打开 URL
// .subscriptionStorePolicyDestination(for: .privacyPolicy, destination: { URL(string: "https://example.com/privacy")! })
// 5. 提供登录入口 (如果你的应用有账户系统,且订阅与账户关联)
.subscriptionStoreSignInAction {
// 处理登录逻辑,例如弹出一个登录视图
print("Sign In button tapped")
// showLoginView = true
}
// 6. (iOS 17.4+, macOS 14.4+) 营销内容自定义
.subscriptionStoreMarketingContent {
// 此闭包允许你提供一个完全自定义的营销内容视图
// 它会替代 StoreKit 默认从 App Store Connect 拉取的营销内容(如果已配置)
// 或者在 SubscriptionStoreView 的内容闭包之外提供主要的营销信息
VStack {
Text("Special Offer!")
.font(.headline)
Text("Get 50% off for the first 3 months.")
.font(.caption)
}
}
// 7. (iOS 17.5+, macOS 14.5+) 自定义视图可见性时的回调
.onSubscriptionStoreViewAppear { subscriptionStoreViewProxy in
print("SubscriptionStoreView did appear. Products: \(subscriptionStoreViewProxy.products.count)")
// subscriptionStoreViewProxy 允许你访问视图内部的一些状态,比如加载的产品
}
.onSubscriptionStoreViewDisappear { subscriptionStoreViewProxy in
print("SubscriptionStoreView did disappear.")
}
// 8. (iOS 17.5+, macOS 14.5+) 购买结果处理
// 虽然 SubscriptionStoreView 内部处理大部分购买逻辑,
// 你仍然可以通过标准的 StoreKit 方式监听交易或使用此回调。
.onInAppPurchaseCompletion { product, result in
// 这个回调在购买流程完成时被调用 (无论成功、失败还是取消)
// 注意:这并不是替代 Transaction.updates 或其他 StoreKit 核心机制,
// 而是 SubscriptionStoreView 特有的一个便捷回调。
// 你仍然需要正确处理 Transaction 和完成交易 (transaction.finish())。
print("Purchase completed for product: \(product.id)")
switch result {
case .success(let verificationResult):
print("Purchase successful (Verification: \(verificationResult))")
// 在这里你可以触发 UI 更新,例如关闭付费墙
// dismiss()
// **重要**: 确保你的 StoreManager 或其他地方仍在监听 Transaction.updates
// 并正确处理和 finish() 该交易。
// 此回调仅为通知,不替代核心交易处理。
Task {
// 假设你有一个 StoreManager 来处理交易和状态更新
// await storeManager.handlePurchaseResult(verificationResult)
}
case .userCancelled:
print("Purchase cancelled by user.")
case .pending:
print("Purchase pending.")
@unknown default:
print("Unknown purchase completion result.")
}
}
// (iOS 17.5+, macOS 14.5+) 也可以只处理成功购买
// .onInAppPurchaseCompletion(for: .success) { product, transaction in
// print("Successfully purchased \(product.displayName)")
// Task {
// await transaction.finish() // 示例:直接完成,但通常应在StoreManager中
// dismiss()
// }
// }
// 添加一个关闭按钮
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Dismiss") {
dismiss()
}
}
}
// .navigationTitle("Go Premium") // 如果在 NavigationView 中
}
}
// 示例:隐私政策和服务条款视图
struct MyPrivacyPolicyView: View {
var body: some View { Text("Your Privacy Policy content here.").padding() }
}
struct MyTermsOfServiceView: View {
var body: some View { Text("Your Terms of Service content here.").padding() }
}
关键点说明:
groupID
: 这是必需的。SubscriptionStoreView
会查找属于此组的所有有效订阅产品。products(for:)
(可选的 StoreKit 方法): 虽然SubscriptionStoreView
会自动加载产品,但你仍然可以使用Product.products(for:)
预先加载产品,或者在StoreManager
中管理它们,SubscriptionStoreView
也能利用这些已加载的产品。- 内容闭包
SubscriptionStoreView { ... }
: 你可以在这个闭包中提供自定义的营销视图 (Header content),例如 App 图标、标题、特性列表等。这部分内容会显示在订阅选项的上方或周围,具体布局取决于所选的样式和平台。 subscriptionStoreButtonLabel
: 控制购买按钮上文本的显示方式,例如是否允许换行。subscriptionStorePickerItemBackground
: 改变每个订阅选项卡片(如果样式是 picker 类型)的背景,可以用于品牌化或提升视觉效果。subscriptionStoreControlStyle
: (iOS 17.2+) 允许你选择不同的控件样式:.automatic
: 系统自动选择最合适的样式。.picker
: 将订阅选项显示为类似 Picker 的列表,用户可以选择其一。.prominentPicker
: 更为突出显示的 Picker 样式,通常每个选项占据更大空间,更像卡片。
subscriptionStorePolicyDestination
: 设置隐私政策和服务条款的跳转目标。这对于符合 App Store 指南至关重要。目标可以是另一个 SwiftUI 视图,也可以是一个 URL。subscriptionStoreSignInAction
: 如果你的订阅是和用户账户绑定的,可以通过这个修饰符提供一个登录入口。subscriptionStoreMarketingContent
(iOS 17.4+): 这个闭包允许你完全自定义显示在订阅选项上方的营销内容区域。如果 App Store Connect 中也配置了营销内容,你提供的视图会优先显示。.onSubscriptionStoreViewAppear
/.onSubscriptionStoreViewDisappear
(iOS 17.5+): 这些回调让你可以在视图出现或消失时执行操作,SubscriptionStoreViewProxy
参数可以提供视图内部加载的产品信息。.onInAppPurchaseCompletion
(iOS 17.5+): 提供了一个便捷的方式来响应从SubscriptionStoreView
内部发起的购买操作的结果。但请记住,这并不取代你的核心 StoreKit 交易处理逻辑 (例如,在StoreManager
中监听Transaction.updates
并调用transaction.finish()
)。 此回调更多是用于 UI 响应,例如购买成功后关闭视图。核心的交易验证和完成仍需遵循标准的 StoreKit 2 实践。
App Store Connect 配置
SubscriptionStoreView
的很多显示内容依赖于你在 App Store Connect 中的配置:
- 订阅组 (Subscription Group): 必须设置。
- 产品 (Products): 在订阅组内创建你的订阅产品(例如,月度、年度)。
- 显示名称和描述 (Display Name & Description): 为每个产品提供清晰的名称和描述,这些会显示在
SubscriptionStoreView
中。 - 价格 (Price Tiers): 设置价格。
- App Store 促销图片 (Promotional Image - 可选): 在订阅组级别或产品级别可以上传促销图片,某些
SubscriptionStoreView
样式可能会展示这些图片。 - 订阅优惠 (Introductory Offers, Promotional Offers): 如果配置了,
SubscriptionStoreView
通常会自动展示这些优惠信息。 - App Store 营销内容 (Marketing Content - 可选,在 App Store Connect 的“App 内购买” -> “营销内容”配置): 你可以为订阅组配置标题和副标题,
SubscriptionStoreView
默认会拉取并展示这部分内容,除非你使用.subscriptionStoreMarketingContent
修饰符提供了自定义内容。
注意事项和限制
- 版本限制: 仅适用于较新的操作系统版本 (iOS 17+ 等)。
- 自定义程度: 虽然提供了一些修饰符,但
SubscriptionStoreView
的整体布局和样式自定义程度不如完全手动构建的 UI。如果你的品牌或设计有非常特殊的需求,可能仍需自定义实现。 - 依赖 App Store Connect: 很多显示内容直接来自 App Store Connect,确保那里的信息是最新的且准确的。
- 测试: 在 Sandbox 环境中充分测试
SubscriptionStoreView
的显示和购买流程。
总而言之,SubscriptionStoreView
是一个非常受欢迎的补充,它显著降低了实现标准订阅购买页面的门槛,使得开发者可以将更多精力放在核心应用功能上,同时还能提供符合苹果设计规范的流畅用户体验。对于大多数需要展示订阅选项的应用来说,它都是一个值得优先考虑的方案。