StoreKit 2:无需额外文件,如何验证已激活自动续订订阅?
StoreKit 2迁移:无Products.plist配置Watch端订阅检查
问题背景
- 已有在App Store Connect配置完成的自动续订订阅应用,正在从SwiftyStoreKit迁移至StoreKit 2
- 不想创建单独的Products.plist文件,需配置WatchStoreManager类检查用户已激活的订阅
- 当前代码抛出
Error Domain=ASDErrorDomain Code=509 "No active account"错误,无法关联现有内购产品
解决方案
核心是直接通过StoreKit 2的API获取App Store Connect中已配置的产品,并验证用户订阅状态,无需依赖本地plist或storekit文件。以下是修改后的完整实现:
完整代码实现
import Foundation import StoreKit typealias Transaction = StoreKit.Transaction public enum StoreError: Error { case failedVerification case productFetchFailed(Error) case noActiveSubscription } @available(watchOSApplicationExtension 8.0, *) class WatchStoreManager: ObservableObject { var updateListenerTask: Task<Void, Error>? = nil // 替换为你在App Store Connect配置的实际订阅产品ID数组 private let subscriptionProductIDs = ["com.yourdomain.your-subscription-id"] @Published var hasActiveSubscription = false init() { print("Init called in WatchStoreManager") updateListenerTask = listenForTransactions() // 初始化时立即检查订阅状态 Task { await checkCurrentSubscriptionStatus() } } deinit { updateListenerTask?.cancel() } func listenForTransactions() -> Task<Void, Error> { return Task.detached { for await result in Transaction.updates { do { let transaction = try self.checkVerified(result) print("已验证交易") await self.updateCustomerProductStatus() await transaction.finish() } catch { print("交易验证失败: \(error.localizedDescription)") } } } } func checkVerified<T>(_ result: VerificationResult<T>) throws -> T { switch result { case .unverified: throw StoreError.failedVerification case .verified(let safe): return safe } } // 直接从App Store获取订阅产品信息 private func fetchSubscriptionProducts() async throws -> [Product] { do { let products = try await Product.products(for: subscriptionProductIDs) return products } catch { throw StoreError.productFetchFailed(error) } } // 检查当前用户的有效订阅状态 func checkCurrentSubscriptionStatus() async { hasActiveSubscription = false do { // 获取用户当前所有未过期的订阅权益 let entitlements = try await Transaction.currentEntitlements for await result in entitlements { guard let transaction = try? checkVerified(result) else { continue } // 验证产品ID匹配且订阅未过期 if subscriptionProductIDs.contains(transaction.productID), transaction.expirationDate?.compare(Date()) == .orderedDescending { hasActiveSubscription = true break } } } catch { print("检查订阅状态失败: \(error.localizedDescription)") } } // 订阅状态更新时同步刷新 func updateCustomerProductStatus() async { await checkCurrentSubscriptionStatus() } }
关键修改说明
- 移除本地产品文件依赖:直接通过
Product.products(for:)方法传入App Store Connect配置的订阅ID数组,无需本地plist/storekit文件 - 订阅状态校验逻辑:
- 用
Transaction.currentEntitlements获取用户当前所有有效订阅权益 - 校验交易产品ID是否匹配、订阅是否未过期,以此判断是否为激活状态
- 用
- 状态实时同步:通过
@Published属性hasActiveSubscription对外暴露订阅状态,方便UI层监听更新 - 错误处理增强:新增产品获取失败、无有效订阅的错误类型,便于调试定位问题
509错误解决要点
- 确保测试设备/模拟器已登录沙盒测试Apple ID(不要用真实ID,可在系统设置中切换)
- 确认App Store Connect中订阅产品状态为「可供销售」,且Bundle ID与Watch App完全一致
- 检查Watch App的签名配置,确保已开启内购权限
- 测试时需连接网络,保证StoreKit能正常访问App Store沙盒服务器
内容的提问来源于stack exchange,提问作者GarySabo
相关产品推荐
相关产品推荐

