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

SwiftUI中使用.fullScreenCover时完成StoreKit购买致应用冻结

SwiftUI内购完成时含.fullScreenCover导致应用冻结的问题解决

已知情况

这个问题确实有不少开发者碰到过,属于SwiftUI和StoreKit交互时的兼容性问题——哪怕fullScreenCover没被触发,只要视图层级里存在这个修饰器,内购完成时就可能触发主线程阻塞,导致应用冻结。目前在iOS 15-17的部分版本中都有反馈记录。

修复方案

以下是几个经过验证的可行方案:

  • 强制在主线程更新状态:StoreKit的回调闭包可能不在主线程执行,直接修改SwiftUI的@State变量会引发线程冲突,进而导致冻结。把状态更新逻辑包裹在DispatchQueue.main.async里,或者用@MainActor标记处理方法。
  • 剥离弹窗触发逻辑:通过Combine的发布者(比如PassthroughSubject)解耦内购处理和弹窗触发,避免直接在回调里修改视图状态。
  • 独立托管fullScreenCover:把fullScreenCover移到更高层级的视图(比如App主视图),通过全局状态或环境对象控制弹窗,避免和内购页面的视图层级绑定。

代码调整示例(方案1)

修改你的onInAppPurchaseCompletion回调,确保状态更新在主线程执行:

struct ShopView: View {
    @EnvironmentObject var store: Store
    
    @State var isFullScreenCoverPresented: Bool = false
    @State var purchasedTip: Tip? = nil
    
    var body: some View {
        VStack {
            VStack {
                ForEach(store.products.sorted(by: { $0.price < $1.price })) { product in
                    ProductView(product) {
                        Image("productImage")
                    }
                }
            }
        }
        .onInAppPurchaseCompletion { product, result in
            if case .success(.success(.verified(let transaction))) = result {
                await store.process(transaction)
                // 确保主线程更新状态
                DispatchQueue.main.async {
                    if let tip = Tip(rawValue: product.id) {
                        purchasedTip = tip
                    }
                    isFullScreenCoverPresented = true
                }
            }
        }
        .fullScreenCover(isPresented: $isFullScreenCoverPresented) {
            if let tip = purchasedTip {
                ThankYouNote(tip: tip)
            }
        }
    }
}

或者用@MainActor标记处理逻辑:

@MainActor
private func handlePurchaseSuccess(product: Product, transaction: Transaction) {
    store.process(transaction)
    if let tip = Tip(rawValue: product.id) {
        purchasedTip = tip
    }
    isFullScreenCoverPresented = true
}

// 在回调中调用
.onInAppPurchaseCompletion { product, result in
    if case .success(.success(.verified(let transaction))) = result {
        await handlePurchaseSuccess(product: product, transaction: transaction)
    }
}

补充说明

如果上述方案无效,可以尝试移除fullScreenCover改用sheet临时替代,或者检查ThankYouNote视图是否存在耗时操作——部分情况下,弹窗视图的初始化逻辑也可能加剧冻结问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:05:03