跳至正文
StoreKit — sync

sync

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() 以确保所有订阅信息是最新的。


方法定义与平台支持

swift
@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. 处理订阅相关逻辑后
    如在用户完成购买、恢复购买或修改订阅计划后调用。

代码示例

swift
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 获取用户当前有效的订阅权益:

    swift
    let entitlements = await Transaction.currentEntitlements
    for case let transaction in entitlements {
        if let subscription = transaction.subscription {
            // 处理订阅信息
        }
    }
    
  • 监听实时更新
    使用 StoreKit.Transaction.updates 监听订阅状态的实时变化:

    swift
    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 时,用于加载并显示用户可以购买的商品及其价格。
    • 在用户准备购买之前,向他们展示清晰的产品信息。
  • 示例:

    swift
    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 在启动时查询通常就足够,所以并非总是必需,过度调用可能没有必要。
    • 当你怀疑本地交易信息可能不是最新的,或者需要强制检查服务器状态时。
  • 示例:

    swift
    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() 则是在需要时(如恢复购买)帮助刷新这些授权信息。

本文共 2248 字,创建于 May 5, 2025

相关标签:Xcode, ByAI, StoreKit

评论

博客助手

正在打开博客助手…