如何在SwiftUI中等待StoreKit2产品加载的异步函数结果?
SwiftUI中正确等待StoreKit2异步加载结果的方案
首先指出你原代码的问题:在View的init里直接调用async throws函数是错误的——init是同步方法,既无法等待异步任务完成,也没法处理抛出的错误,而且直接在初始化阶段操作@State变量可能引发SwiftUI视图状态异常。
下面是两种靠谱的实现方案:
方案一:使用.task修饰符(SwiftUI 3+推荐)
.task是SwiftUI专门为异步任务设计的修饰符,会在视图出现时自动执行异步逻辑,视图消失时自动取消任务,还能自然处理错误:
struct SettingsForm : View { @State var selectedRegion: Int = 0 @State var alwaysOriginalTitle: Bool = false @State private var products: [Product] = [] @State private var loadError: Error? // 存储加载错误 @Environment(\.dismiss) var dismiss // 替代旧的presentationMode let productIds = ["premium"] var body: some View { Form { // 你的原有表单控件... // 根据加载状态展示不同内容 if let error = loadError { Text("产品加载失败:\(error.localizedDescription)") .foregroundColor(.red) } else if products.isEmpty { ProgressView("加载中...") } else { // 加载完成后展示产品 ForEach(products, id: \.id) { product in Text("\(product.displayName) - \(product.displayPrice)") } } } .task { do { // 等待异步加载完成并赋值 products = try await Product.products(for: productIds) print(products) } catch { // 捕获并存储错误 loadError = error print("加载出错:\(error)") } } } }
方案二:分离逻辑到ViewModel(复杂场景适用)
如果需要更复杂的状态管理,把加载逻辑抽离到ObservableObject类型的ViewModel中,符合MVVM架构:
class StoreViewModel: ObservableObject { @Published var products: [Product] = [] @Published var loadError: Error? let productIds = ["premium"] func loadProducts() async { do { products = try await Product.products(for: productIds) print(products) } catch { loadError = error print("加载出错:\(error)") } } } struct SettingsForm : View { @State var selectedRegion: Int = 0 @State var alwaysOriginalTitle: Bool = false @StateObject private var storeVM = StoreViewModel() @Environment(\.dismiss) var dismiss var body: some View { Form { // 你的原有表单控件... if let error = storeVM.loadError { Text("产品加载失败:\(error.localizedDescription)") .foregroundColor(.red) } else if storeVM.products.isEmpty { ProgressView("加载中...") } else { ForEach(storeVM.products, id: \.id) { product in Text("\(product.displayName) - \(product.displayPrice)") } } } .task { await storeVM.loadProducts() } } }
关键注意点
- 永远不要在View的初始化方法里调用异步函数,SwiftUI视图是值类型,初始化阶段操作状态会引发不可预期的问题。
Product.products(for:)会抛出错误(比如网络异常、产品ID无效),必须用do-catch捕获处理。- 加载过程中要给用户反馈(比如
ProgressView),避免界面无响应感。
内容的提问来源于stack exchange,提问作者riki
相关产品推荐
相关产品推荐

