Synchronizes your app’s transaction information and subscription status with information from the App Store.
StoreKit
框架中的 sync()
方法是 Apple 提供的一个用于同步用户应用内购买(特别是订阅)状态的重要方法。它属于 AppStore
协议,旨在确保本地设备与 App Store 服务器之间的订阅状态一致。以下是详细介绍:
功能与用途 #
同步订阅状态
当用户在不同设备上购买或修改订阅(如升级、降级、取消自动续订)时,本地设备可能无法立即获取最新状态。sync()
方法会主动触发与 App Store 服务器的通信,更新本地存储的订阅信息,确保应用内显示的订阅状态与服务器一致。恢复购买后调用
如果用户通过restorePurchases()
恢复购买,建议随后调用sync()
以确保所有订阅信息是最新的。
方法定义与平台支持 #
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public static func sync() async
- 平台要求:iOS 14.0+、macOS 11.0+、tvOS 14.0+、watchOS 7.0+。
- 异步方法:需在
async
上下文中使用await
调用。
使用场景 #
- 应用启动时检查订阅状态
在应用启动时调用sync()
,确保用户打开应用时看到的订阅信息是最新的。 - 用户主动触发刷新
例如,在设置界面添加“刷新订阅状态”按钮,供用户手动同步。 - 处理订阅相关逻辑后
如在用户完成购买、恢复购买或修改订阅计划后调用。
代码示例 #
import StoreKit
// 在异步上下文中调用
Task {
do {
try await AppStore.sync()
print("Subscription status synced successfully.")
// 更新本地订阅状态(需结合其他 API,如 currentEntitlements)
} catch {
print("Failed to sync subscription status: \(error)")
}
}
注意事项 #
- 用户登录状态
需要用户已登录其 Apple ID。如果用户未登录,sync()
可能无法正确同步。 - 网络连接
需确保设备有网络连接,否则同步会失败。 - 无返回值
sync()
本身不会返回具体的订阅信息,需结合Transaction.currentEntitlements
或监听StoreKit.Transaction.updates
获取最新状态。 - 沙盒环境测试
在测试时,订阅状态的同步可能会有延迟(沙盒环境通常延迟较短,但非实时)。
与其他 API 的配合 #
获取最新订阅信息
同步后,可通过Transaction.currentEntitlements
获取用户当前有效的订阅权益:let entitlements = await Transaction.currentEntitlements for case let transaction in entitlements { if let subscription = transaction.subscription { // 处理订阅信息 } }
监听实时更新
使用StoreKit.Transaction.updates
监听订阅状态的实时变化:Task { for await update in StoreKit.Transaction.updates { // 处理更新 } }
常见问题 #
同步失败如何处理?
捕获错误并根据具体原因提示用户(如网络问题、未登录 Apple ID 等)。是否需要频繁调用?
通常不需要。Apple 建议仅在必要时(如用户主动操作后)调用。
通过合理使用 sync()
,开发者可以有效管理订阅状态的准确性,提升用户体验。更多细节可参考 Apple 官方文档。
AppStore.sync 与 Product.products 区别 #
在 StoreKit 2 中,AppStore.sync()
和 Product.products(for:)
是两个功能和目的截然不同的异步方法。
Product.products(for: Set<String>)
#
目的: 获取你在 App Store Connect 中配置的产品的详细信息(元数据)。
作用:
- 当你需要向用户展示可购买的商品(例如,在付费墙、商店页面)时,你会使用此方法。
- 它会根据你提供的一组产品 ID (Product Identifiers),向 App Store 服务器请求这些产品的具体信息。
- 返回的信息包括:
- 本地化的产品显示名称 (
product.displayName
) - 本地化的产品描述 (
product.description
) - 本地化的价格 (
product.displayPrice
) - 产品类型 (
product.type
,例如.autoRenewable
,.nonConsumable
,.consumable
) - 订阅信息(如果产品是订阅类型),如订阅周期 (
product.subscription.subscriptionPeriod
)、促销优惠 (product.subscription.introductoryOffer
) 等。
- 本地化的产品显示名称 (
输入: 一个包含产品 ID 字符串的
Set<String>
。这些 ID 是你在 App Store Connect 中为每个应用内购买项目设置的唯一标识符。输出: 一个
[Product]
数组。每个Product
结构体都包含了对应产品 ID 的详细信息。如果某些 ID 无效或找不到,它们将不会出现在返回的数组中,SKProductsResponse
(在 StoreKit 1 中) 中的invalidProductIdentifiers
在 StoreKit 2 中是通过捕获错误或检查返回数组的完整性来间接处理的。通常,如果提供的 ID 集合中有无法识别的 ID,该方法可能会成功返回有效 ID 的产品,或者在某些情况下如果所有 ID 都无效则可能抛出错误。何时使用:
- 在构建付费墙或商店 UI 时,用于加载并显示用户可以购买的商品及其价格。
- 在用户准备购买之前,向他们展示清晰的产品信息。
示例:
import StoreKit @MainActor class ProductFetcher: ObservableObject { @Published var availableProducts: [Product] = [] let productIDs = ["com.example.myapp.monthlySub", "com.example.myapp.yearlySub", "com.example.myapp.premiumFeature"] func fetchProducts() async { do { let storeProducts = try await Product.products(for: productIDs) self.availableProducts = storeProducts print("Fetched \(storeProducts.count) products.") for product in storeProducts { print("Product: \(product.displayName) - Price: \(product.displayPrice)") } } catch { print("Failed to fetch products: \(error)") } } }
AppStore.sync()
#
目的: 与 App Store 同步用户的交易状态和授权信息。
作用:
- 这个方法会主动联系 App Store 服务器,请求更新当前设备的交易记录。
- 它用于确保你的应用拥有用户所有购买(包括在其他设备上使用同一 Apple ID 进行的购买或在应用外管理的订阅更改)的最新状态。
- 调用
AppStore.sync()
后,任何新的或已更新的交易通常会通过Transaction.updates
这个异步序列被传递给你的应用。同时,Transaction.currentEntitlements
也会反映出最新的授权状态。 - 它不直接返回产品元数据(如名称、价格)。它的主要目的是刷新交易和授权。
输入:
AppStore.SyncOptions
(可选)。默认情况下,你不需要提供任何选项,直接调用try await AppStore.sync()
即可。输出:
Void
(即不返回特定值,但如果同步失败会抛出错误)。它的主要效果是触发Transaction.updates
和更新Transaction.currentEntitlements
的内容。何时使用:
- 实现“恢复购买” (Restore Purchases) 功能: 这是最常见的用例。当用户点击“恢复购买”按钮时,调用此方法。
- 应用启动或进入前台时 (可选但需谨慎): 某些开发者可能会在应用启动时调用它来确保状态是最新的。但由于
Transaction.updates
会自动监听变化,且Transaction.currentEntitlements
在启动时查询通常就足够,所以并非总是必需,过度调用可能没有必要。 - 当你怀疑本地交易信息可能不是最新的,或者需要强制检查服务器状态时。
示例:
import StoreKit @MainActor class PurchaseRestorer: ObservableObject { @Published var restorationError: Error? @Published var restorationCompleted: Bool = false // 确保你的 StoreManager 或其他地方正在监听 Transaction.updates // 和使用 Transaction.currentEntitlements 来更新用户授权 func restorePurchases() async { restorationCompleted = false restorationError = nil print("Attempting to sync transactions with the App Store...") do { try await AppStore.sync() // 同步成功后,你的 Transaction.updates 监听器和 // 对 Transaction.currentEntitlements 的检查应该会获取到恢复的购买。 // 你通常不需要在这里直接处理交易,而是依赖你的全局交易监听逻辑。 print("AppStore.sync() completed. Check Transaction.updates or currentEntitlements.") restorationCompleted = true } catch { print("Failed to sync transactions: \(error)") restorationError = error } } }
主要区别总结 #
特性 | Product.products(for:) | AppStore.sync() |
主要目的 | 获取产品元数据 (名称, 描述, 价格等) | 同步用户的交易和授权状态 |
通信内容 | 向 App Store 请求产品目录信息 | 向 App Store 请求更新本地的交易记录,检查是否有新的或已更改的购买/订阅 |
返回/影响 | 返回 [Product] 数组,包含产品详情 | 不直接返回值,但会触发 Transaction.updates 和更新 Transaction.currentEntitlements |
使用场景 | 构建商店/付费墙 UI,展示可购买项 | 实现“恢复购买”功能,确保应用拥有最新的用户授权信息 |
数据类型 | 处理的是 Product (商品信息) | 处理的是 Transaction (交易信息) 和用户的授权状态 |
是否改变用户授权 | 否,仅获取信息 | 是,可能会导致应用识别到新的或已恢复的授权 |
简单来说:
- 你想知道有哪些东西可以卖以及它们的价格 -> 使用
Product.products(for:)
。 - 你想知道用户已经买了什么,或者确保应用拥有用户最新的购买记录 -> 使用
AppStore.sync()
(通常用于恢复购买,并依赖Transaction.updates
和Transaction.currentEntitlements
来获取结果)。
这两个方法在 StoreKit 2 的购买流程中扮演着不同但互补的角色。你通常会先用 Product.products(for:)
展示商品,用户购买后,你会通过 Transaction.updates
和 Transaction.currentEntitlements
来管理他们的授权,而 AppStore.sync()
则是在需要时(如恢复购买)帮助刷新这些授权信息。