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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:25:22