SwiftUI+StoreKit2:修复purchase方法缺失错误并实现启动加载产品
问题修复方案
1. 修复purchase方法缺失的错误
当前SettingsForm结构体中未定义purchase方法,需添加异步方法处理内购流程,包含购买调用、交易验证与完成逻辑:
private func purchase(_ product: Product) async throws { let purchaseResult = try await product.purchase() switch purchaseResult { case .success(let verification): switch verification { case .verified(let transaction): // 验证通过,可在此添加解锁高级功能等业务逻辑 await transaction.finish() case .unverified(_, let error): // 验证失败,抛出错误供上层处理 throw error } case .userCancelled: // 用户主动取消购买,无需额外操作 break case .pending: // 购买待处理(如需要家长审批),可提示用户等待 break @unknown default: // 兼容未来可能新增的状态 break } }
2. 实现视图启动时自动加载内购产品
在onAppear回调中通过Task异步调用loadProducts方法,确保视图出现时自动拉取内购项目:
修改原onAppear代码为:
.onAppear { if let index = NSLocale.isoCountryCodes.firstIndex(of: AppUserDefaults.region) { self.selectedRegion = index } self.alwaysOriginalTitle = AppUserDefaults.alwaysOriginalTitle // 异步加载内购产品 Task { do { try await loadProducts() } catch { print("加载内购产品失败: \(error.localizedDescription)") } } }
3. 优化ForEach参数命名(可选但推荐)
原代码中ForEach参数名与数组名重复,建议修改为更清晰的命名避免混淆:
ForEach(self.products) { product in Button { Task { do { try await self.purchase(product) } catch { print(error) } } } label: { Text("\(product.displayPrice) - \(product.displayName)") } }
完整修改后的SettingsForm代码
import SwiftUI import Foundation import Backend import StoreKit @available(iOS 15.0, *) struct SettingsForm : View { @State var selectedRegion: Int = 0 @State var alwaysOriginalTitle: Bool = false @State private var products: [Product] = [] @Environment(\.presentationMode) var presentationMode let productIds = ["premium"] private func loadProducts() async throws { self.products = try await Product.products(for: productIds) print(self.products) } private func purchase(_ product: Product) async throws { let purchaseResult = try await product.purchase() switch purchaseResult { case .success(let verification): switch verification { case .verified(let transaction): await transaction.finish() // 此处添加购买成功后的业务逻辑,例如解锁高级功能 case .unverified(_, let error): throw error } case .userCancelled: break case .pending: break @unknown default: break } } var countries: [String] { get { var countries: [String] = [] for code in NSLocale.isoCountryCodes { let id = NSLocale.localeIdentifier(fromComponents: [NSLocale.Key.countryCode.rawValue: code]) let name = NSLocale(localeIdentifier: "en_US").displayName(forKey: NSLocale.Key.identifier, value: id)! countries.append(name) } return countries } } func debugInfoView(title: String, info: String) -> some View { HStack { Text(title) Spacer() Text(info).font(.body).foregroundColor(.secondary) } } var body: some View { NavigationView { Form { Section(header: Text("Abbonamenti"), footer: Text(""), content: { ForEach(self.products) { product in Button { Task { do { try await self.purchase(product) } catch { print(error) } } } label: { Text("\(product.displayPrice) - \(product.displayName)") } } }) Section(header: Text("Region preferences"), footer: Text("Region is used to display a more accurate movies list"), content: { Toggle(isOn: $alwaysOriginalTitle) { Text("Always show original title") } Picker(selection: $selectedRegion, label: Text("Region"), content: { ForEach(0 ..< self.countries.count) { Text(self.countries[$0]).tag($0) } }) }) Section(header: Text("App data"), footer: Text("None of those action are working yet ;)"), content: { Text("Export my data") Text("Backup to iCloud") Text("Restore from iCloud") Text("Reset application data").foregroundColor(.red) }) Section(header: Text("Debug info")) { debugInfoView(title: "Movies in state", info: "\(store.state.moviesState.movies.count)") debugInfoView(title: "Archived state size", info: "\(store.state.sizeOfArchivedState())") } } .onAppear{ if let index = NSLocale.isoCountryCodes.firstIndex(of: AppUserDefaults.region) { self.selectedRegion = index } self.alwaysOriginalTitle = AppUserDefaults.alwaysOriginalTitle Task { do { try await loadProducts() } catch { print("加载内购产品失败: \(error.localizedDescription)") } } } .navigationBarItems( leading: Button(action: { self.presentationMode.wrappedValue.dismiss() }, label: { Text("Cancel").foregroundColor(.red) }), trailing: Button(action: { AppUserDefaults.region = NSLocale.isoCountryCodes[self.selectedRegion] AppUserDefaults.alwaysOriginalTitle = self.alwaysOriginalTitle self.presentationMode.wrappedValue.dismiss() }, label: { Text("Save") })) .navigationBarTitle(Text("Settings")) } } } #if DEBUG struct SettingsForm_Previews : PreviewProvider { @available(iOS 14.0, *) static var previews: some View { if #available(iOS 15.0, *) { SettingsForm() } else { // Fallback on earlier versions } } } #endif
内容的提问来源于stack exchange,提问作者riki
相关产品推荐
相关产品推荐

