Segue/实例化Stripe CheckoutViewController时初始化错误及数据传递问题求助
看起来你遇到的问题核心是Storyboard初始化ViewController时会调用init?(coder:),而这个方法的初始化逻辑没有符合Swift的存储属性初始化规则。让我一步步帮你理清问题并给出解决方案:
为什么会出现这些错误?
第一个错误:
init(coder:) has not been implemented
当你把ViewController关联到Storyboard后,系统会通过init?(coder:)来初始化这个VC,而你最初的实现里这个方法只是简单调用了super.init(coder:),没有初始化config这个let类型的存储属性——Swift要求所有存储属性必须在调用父类初始化方法前完成初始化,所以直接报错。第二个错误:
Property 'self.paymentContext' not initialized at super.init call
你后来修改代码时,可能把super.init(coder:)放在了属性赋值之前,对于let类型的config来说,这违反了Swift的初始化规则:父类初始化必须在所有子类存储属性初始化之后调用。
正确的初始化实现方案
我们需要同时兼容手动实例化(init())和Storyboard实例化(init?(coder:)),并且把重复的配置逻辑抽离出来,避免代码冗余:
import UIKit import Stripe class CheckoutViewController: UIViewController, STPPaymentContextDelegate { // 把paymentContext改为var,方便后续更新支付信息 var paymentContext: STPPaymentContext? // let类型的config必须在super.init前初始化 let config: STPPaymentConfiguration let customerContext = STPCustomerContext(keyProvider: MyAPIClient()) // 手动实例化的初始化方法 init() { self.config = STPPaymentConfiguration() super.init(nibName: nil, bundle: nil) setupPaymentContext() } // Storyboard实例化的初始化方法(必须实现) required init?(coder aDecoder: NSCoder) { self.config = STPPaymentConfiguration() super.init(coder: aDecoder) setupPaymentContext() } // 抽离共同的支付上下文配置逻辑 private func setupPaymentContext() { // 配置账单地址要求 config.requiredBillingAddressFields = .name // 创建支付上下文 let context = STPPaymentContext(customerContext: customerContext, configuration: config, theme: .defaultTheme) context.delegate = self context.hostViewController = self self.paymentContext = context } // MARK: - STPPaymentContextDelegate 实现(示例) func paymentContextDidChange(_ paymentContext: STPPaymentContext) { // 处理支付上下文变化 } func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) { // 处理加载失败 } func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock) { // 处理支付结果 } }
如何通过Segue传递支付数据?
你的核心需求是传递支付金额等数据,只需在CheckoutViewController中添加对应的属性,然后在源ViewController的prepare(for:sender:)方法中赋值即可:
1. 在CheckoutViewController中添加支付信息属性
// 添加支付金额(Stripe使用最小货币单位,比如1000代表10美元) var paymentAmount: Int = 0 // 支付货币代码,默认USD var paymentCurrency: String = "USD"
2. 在源ViewController的Segue准备方法中传递数据
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { guard let checkoutVC = segue.destination as? CheckoutViewController else { return } // 传递支付金额和货币 checkoutVC.paymentAmount = 1500 // 15美元 checkoutVC.paymentCurrency = "USD" // 更新支付上下文的订单信息 checkoutVC.paymentContext?.paymentAmount = checkoutVC.paymentAmount checkoutVC.paymentContext?.paymentCurrency = checkoutVC.paymentCurrency }
你之前修改的代码为什么不可行?
你尝试的代码把super.init(coder:)放在了属性赋值的最后,但存在两个问题:
- 虽然局部变量赋值给
self.config是在super.init之前,但直接在初始化方法中设置paymentContext?.delegate = self时,self还未完全初始化完成,可能引发潜在问题; - 代码没有抽离重复逻辑,手动实例化和Storyboard实例化的配置代码冗余,后续维护成本高。
按照上面的方案实现,既可以解决初始化错误,又能顺利通过Segue传递支付数据,满足你的需求。
内容的提问来源于stack exchange,提问作者dsonawave

