StoreKit — sync
This article is extracted from the chat log with AI. Please identify it with caution.

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 调用。

使用场景 #

  1. 应用启动时检查订阅状态
    在应用启动时调用 sync(),确保用户打开应用时看到的订阅信息是最新的。
  2. 用户主动触发刷新
    例如,在设置界面添加“刷新订阅状态”按钮,供用户手动同步。
  3. 处理订阅相关逻辑后
    如在用户完成购买、恢复购买或修改订阅计划后调用。

代码示例 #

import StoreKit

// 在异步上下文中调用
Task {
    do {
        try await AppStore.sync()
        print("Subscription status synced successfully.")
        // 更新本地订阅状态(需结合其他 API,如 currentEntitlements)
    } catch {
        print("Failed to sync subscription status: \(error)")
    }
}

注意事项 #

  1. 用户登录状态
    需要用户已登录其 Apple ID。如果用户未登录,sync() 可能无法正确同步。
  2. 网络连接
    需确保设备有网络连接,否则同步会失败。
  3. 无返回值
    sync() 本身不会返回具体的订阅信息,需结合 Transaction.currentEntitlements 或监听 StoreKit.Transaction.updates 获取最新状态。
  4. 沙盒环境测试
    在测试时,订阅状态的同步可能会有延迟(沙盒环境通常延迟较短,但非实时)。

与其他 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.updatesTransaction.currentEntitlements 来获取结果)。

这两个方法在 StoreKit 2 的购买流程中扮演着不同但互补的角色。你通常会先用 Product.products(for:) 展示商品,用户购买后,你会通过 Transaction.updatesTransaction.currentEntitlements 来管理他们的授权,而 AppStore.sync() 则是在需要时(如恢复购买)帮助刷新这些授权信息。

本文共 2653 字,创建于 May 5, 2025
相关标签: Xcode, ByAI, StoreKit