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
相关产品推荐
相关产品推荐

