能否实现项目内ViewController向Pod库中ViewController的数据传递?(支付SDK场景)
解决项目ViewController向Pod库ViewController传递数据的方案
我之前对接第三方支付SDK时也碰到过类似的需求,给你几个实际可行的方案,你可以根据SDK的开源情况选择:
方案1:利用SDK自带的配置类/初始化参数(优先推荐)
很多成熟的SDK会在初始化核心视图控制器时提供配置入口,用来传递自定义参数。你可以先仔细查看SDK的文档或头文件,看看有没有类似PaymentConfig、SDKSetting这类类,里面是否支持预填银行卡信息的字段。
举个Swift的例子:
// 项目内的ViewController中 import PaymentSDK // 初始化SDK配置类(如果SDK提供的话) let paymentConfig = PaymentSDKConfiguration() paymentConfig.preFilledCardHolder = "张三" paymentConfig.preFilledCardNumber = "4111****1111" // 用配置类初始化SDK的ViewController let paymentVC = PaymentSDKViewController(config: paymentConfig) present(paymentVC, animated: true)
如果SDK本身没有提供这类配置,再考虑下面的方案。
方案2:通过关联对象(Associated Objects)动态添加属性(适用于开源SDK)
如果SDK是开源的,你可以给SDK的ViewController动态添加属性,用来接收项目传递的数据。关联对象能让你在不修改原类代码的前提下,给类添加额外的属性。
步骤如下:
- 在项目中创建SDK ViewController的扩展:
import PaymentSDK // 定义关联对象的Key(必须是全局唯一的) private var CardHolderAssociatedKey: UInt8 = 0 private var CardNumberAssociatedKey: UInt8 = 0 extension PaymentSDKViewController { // 动态添加预填持卡人姓名的属性 var preFilledCardHolder: String? { get { objc_getAssociatedObject(self, &CardHolderAssociatedKey) as? String } set { objc_setAssociatedObject(self, &CardHolderAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } // 动态添加预填卡号的属性 var preFilledCardNumber: String? { get { objc_getAssociatedObject(self, &CardNumberAssociatedKey) as? String } set { objc_setAssociatedObject(self, &CardNumberAssociatedKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
- 在项目的ViewController中给SDK的VC赋值:
let paymentVC = PaymentSDKViewController() paymentVC.preFilledCardHolder = "张三" paymentVC.preFilledCardNumber = "4111****1111" present(paymentVC, animated: true)
- 在SDK的
PaymentSDKViewController的viewDidLoad或合适的生命周期方法中,读取这些属性并填充到输入框:
override func viewDidLoad() { super.viewDidLoad() // 填充预填数据 cardHolderTextField.text = preFilledCardHolder cardNumberTextField.text = preFilledCardNumber }
方案3:利用通知中心传递数据(适用于闭源SDK,需SDK支持或自行Hook)
如果SDK是闭源的,你没法直接修改它的代码,可以用通知中心来传递数据。
- 在项目的ViewController中发送通知:
// 构造要传递的数据 let cardInfo = [ "cardHolder": "张三", "cardNumber": "4111****1111" ] // 发送通知 NotificationCenter.default.post( name: NSNotification.Name("PreFillBankCardInfo"), object: nil, userInfo: cardInfo ) // 弹出SDK的支付VC present(paymentVC, animated: true)
- 这里的关键是让SDK的VC能监听这个通知。如果SDK是闭源的,你需要用方法交换(Method Swizzling)来替换SDK VC的生命周期方法,添加通知监听:
import PaymentSDK extension PaymentSDKViewController { static func setupNotificationObserver() { let originalSelector = #selector(viewDidLoad) let swizzledSelector = #selector(swizzled_viewDidLoad) guard let originalMethod = class_getInstanceMethod(self, originalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) } @objc func swizzled_viewDidLoad() { // 先调用原有的viewDidLoad逻辑 swizzled_viewDidLoad() // 添加通知监听 NotificationCenter.default.addObserver( self, selector: #selector(handlePreFillCardInfo(_:)), name: NSNotification.Name("PreFillBankCardInfo"), object: nil ) } @objc func handlePreFillCardInfo(_ notification: Notification) { guard let userInfo = notification.userInfo, let holder = userInfo["cardHolder"] as? String, let number = userInfo["cardNumber"] as? String else { return } // 通过KVC设置输入框内容(需要知道输入框的属性名) setValue(holder, forKeyPath: "cardHolderTextField.text") setValue(number, forKeyPath: "cardNumberTextField.text") } deinit { NotificationCenter.default.removeObserver(self) } }
- 在项目启动时(比如
AppDelegate的didFinishLaunchingWithOptions)执行方法交换:
PaymentSDKViewController.setupNotificationObserver()
方案4:方法交换+KVC直接赋值(闭源SDK的终极方案)
如果上面的通知方案也不合适,你可以直接通过方法交换,在SDK VC的生命周期方法里,用KVC给输入框赋值。前提是你要知道SDK输入框的属性名(可以通过反编译工具查看,比如Hopper)。
示例代码:
import PaymentSDK // 先定义一个单例来存储要传递的银行卡数据 class CardDataManager { static let shared = CardDataManager() var cardHolder: String? var cardNumber: String? } // 在项目的ViewController中存储数据 CardDataManager.shared.cardHolder = "张三" CardDataManager.shared.cardNumber = "4111****1111" // 然后给SDK的VC做方法交换 extension PaymentSDKViewController { static func setupSwizzling() { let originalSelector = #selector(viewDidLoad) let swizzledSelector = #selector(swizzled_viewDidLoad) guard let originalMethod = class_getInstanceMethod(self, originalSelector), let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) } @objc func swizzled_viewDidLoad() { swizzled_viewDidLoad() let cardData = CardDataManager.shared // 用KVC设置输入框内容 if let holder = cardData.cardHolder { setValue(holder, forKeyPath: "cardHolderTextField.text") } if let number = cardData.cardNumber { setValue(number, forKeyPath: "cardNumberTextField.text") } } }
然后在项目启动时执行交换:
PaymentSDKViewController.setupSwizzling()
⚠️ 注意:KVC访问私有属性有风险,如果SDK更新后属性名变化,会导致崩溃,建议添加容错处理,比如用value(forKey:)先判断属性是否存在。
内容的提问来源于stack exchange,提问作者Zyfe3r
相关产品推荐
相关产品推荐

