You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI自动续订订阅测试异常:isPremium莫名变为false求助

自动续订订阅isPremium莫名变false的原因及解决办法

1. StoreKit测试订阅到期

你使用的StoreKit配置文件中,测试订阅是带有固定到期时间的(比如5分钟、1小时),到期后StoreKit会自动标记订阅失效,purchasedSubscriptions数组会移除对应产品,此时isPurchased返回false,isPremium就会被重置为false。

解决办法:

  • 打开StoreKit配置文件,编辑订阅产品的测试订阅期限,改为更长时长(比如1天),或者设置为「永不过期」(仅适用于测试场景);
  • 订阅到期后,通过Xcode的「Window > StoreKit > StoreKit Test」窗口手动续订测试订阅。

2. purchasedSubscriptions未实时同步订阅状态

你的isPurchased逻辑依赖purchasedSubscriptions数组,但如果这个数组没有监听StoreKit的订阅状态变化(比如续订、过期、退款),就会出现状态不同步,导致检查结果错误。

解决办法:
在StoreManager中监听Transaction.updates流,实时更新purchasedSubscriptions:

class StoreManager: ObservableObject {
    @Published var purchasedSubscriptions: [Product] = []
    @Published var subscriptions: [Product] = []
    
    init() {
        Task {
            await loadSubscriptions()
            await listenForTransactionUpdates()
        }
    }
    
    private func loadSubscriptions() async {
        do {
            // 替换为你的订阅产品ID数组
            subscriptions = try await Product.products(for: ["com.yourapp.premium"])
        } catch {
            print("加载订阅产品失败:\(error)")
        }
    }
    
    private func listenForTransactionUpdates() async {
        for await transaction in Transaction.updates {
            await updatePurchasedSubscriptions()
        }
    }
    
    private func updatePurchasedSubscriptions() async {
        var purchased: [Product] = []
        for await result in Transaction.currentEntitlements {
            guard case .success(let transaction) = result else { continue }
            if let product = subscriptions.first(where: { $0.id == transaction.productID }) {
                purchased.append(product)
            }
            await transaction.finish()
        }
        purchasedSubscriptions = purchased
    }
}

3. load()函数仅执行一次,未响应状态变化

当前load()只在ContentView的.task中执行一次,但订阅状态发生变化(比如续订、过期)时,不会重新调用load()更新isPremium,导致isPremium停留在旧值,或后续被错误覆盖。

解决办法:
监听storeManager.purchasedSubscriptions的变化,自动触发load()更新isPremium:

struct ContentView: View {
    @EnvironmentObject private var storeManager: StoreManager
    @AppStorage("isPremium") private var isPremium = false

    var body: some View {
        TabView {
            FirstView()
            SecondView()
        }
        .task { await load() }
        .onChange(of: storeManager.purchasedSubscriptions) { _ in
            Task { await load() }
        }
    }

    private func load() async {
        guard let subscription = storeManager.subscriptions.first else { return }
        isPremium = (try? await storeManager.isPurchased(subscription)) ?? isPremium
    }
}

4. Product实例的Equatable比较逻辑问题

默认情况下,Product的contains比较可能基于实例引用而非产品ID,如果storeManager.subscriptions中的Product实例和purchasedSubscriptions中的不是同一个,contains会返回false。

解决办法:
修改isPurchased函数,基于产品ID判断订阅状态:

func isPurchased(_ product: Product) async throws -> Bool {
    return purchasedSubscriptions.contains(where: { $0.id == product.id })
}

内容的提问来源于stack exchange,提问作者Alexnnd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:55:32