Swift 集成 Objective-C HDFC iOS SDK 时崩溃
结论
问题不在于 Storyboard 与 XIB 混用,也不是 Bridging Header 导致的。崩溃表明,HDFC SDK 的 Interface Builder 文件与运行时实际加载的 Objective-C 类不一致:
Unknown class CreditCardCell:运行时找不到CreditCardCell的实现。常见原因是.m文件或静态库未被链接,或者 XIB 中的 Custom Class 配置有误。not key value coding-compliant for the key makePaymentButton:XIB 中仍保留名为makePaymentButton的 IBOutlet 连接,但实例化的对象没有这个属性,或者连接到了错误的对象。
类加载和 IBOutlet 连接都要修复。只重新拖拽控件引用,或只修改 Bridging Header,都无法解决问题。
先确认 CreditCardCell 确实进入应用
Bridging Header 只是让 Swift 在编译阶段看到 Objective-C 声明。它不会自动编译 .m 文件,也不会自动复制 XIB。
如果 SDK 以源码形式提供,至少应包含:
// CreditCardCell.h
#import <UIKit/UIKit.h>
@interface CreditCardCell : UITableViewCell
@end
// CreditCardCell.m
#import "CreditCardCell.h"
@implementation CreditCardCell
@end
逐项检查:
CreditCardCell.m已加入当前 App Target。- Target 的 Build Phases → Compile Sources 中包含
CreditCardCell.m。 CreditCardCell.xib已勾选正确的 Target Membership。- Build Phases → Copy Bundle Resources 中包含
CreditCardCell.xib。 - 如果 HDFC SDK 是静态库,对应的
.a或 framework 已加入 Link Binary With Libraries。 - Bridging Header 只需导入需要向 Swift 暴露的头文件:
#import "CreditCardCell.h"
#import "PaymentViewController.h"
如果 SDK 是静态库,类仍未被加载,可以根据 SDK 的打包方式,在 Other Linker Flags 中尝试加入:
-ObjC
先不要使用 -all_load。它可能引发重复符号问题,只有确认静态库存在链接裁剪时才需要考虑。
修正 CreditCardCell.xib 的 Custom Class
打开 CreditCardCell.xib,选中最外层的 table view cell,在 Identity Inspector 中确认:
Class: CreditCardCell
如果 CreditCardCell 是 Objective-C 类,Module 通常留空,或者选择该类实际所属的 framework/module。不要误填 Swift App 的模块名。
还要检查:
- XIB 的根对象确实是
UITableViewCell。 - Reuse Identifier 与代码中使用的标识一致。
- Custom Class 没有误配到
File's Owner、Content View 或其他对象。 - XIB 中的类名与 Objective-C 类名大小写完全一致。
可以用下面的代码检查 Objective-C 运行时能否找到该类:
if NSClassFromString("CreditCardCell") == nil {
assertionFailure("CreditCardCell has not been linked into the application")
}
如果 SDK 将类放在动态 framework 中,运行时名称也可能带有模块前缀。具体名称由 SDK 的打包方式决定,不能只根据 Bridging Header 判断。
正确加载 Cell 的 XIB
如果由项目代码注册这个 Cell,应从包含该类和 XIB 的 bundle 加载,不要固定使用 Bundle.main:
let sdkBundle = Bundle(for: CreditCardCell.self)
let nib = UINib(nibName: "CreditCardCell", bundle: sdkBundle)
tableView.register(
nib,
forCellReuseIdentifier: "CreditCardCell"
)
使用时:
guard let cell = tableView.dequeueReusableCell(
withIdentifier: "CreditCardCell",
for: indexPath
) as? CreditCardCell else {
fatalError("CreditCardCell.xib or its reuse identifier is configured incorrectly")
}
如果 XIB 位于独立的 resource bundle 中,就要从对应的 resource bundle 加载,不能用上面的 Bundle(for:) 假设资源和类位于同一个 bundle。
修复 makePaymentButton 连接
NSUnknownKeyException 表示 XIB 解档时尝试执行类似操作:
[object setValue:button forKey:@"makePaymentButton"];
但 object 对应的类没有 makePaymentButton 属性。
打开崩溃时加载的所有相关 XIB,重点检查 Payment Screen 的主 XIB 和 CreditCardCell.xib。在 Connections Inspector 中查找 makePaymentButton,再根据按钮的实际归属进行处理。
按钮属于 Payment View Controller
控制器头文件应声明这个 IBOutlet:
// PaymentViewController.h
@interface PaymentViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIButton *makePaymentButton;
@end
XIB 中应这样配置:
- 将
File's Owner的 Custom Class 设置为PaymentViewController。 - 将按钮连接到
File's Owner的makePaymentButton。 - 不要将这个连接挂到
CreditCardCell、根UIView或其他对象上。
创建控制器时也要使用正确的 bundle:
let bundle = Bundle(for: PaymentViewController.self)
let paymentViewController = PaymentViewController(
nibName: "PaymentViewController",
bundle: bundle
)
navigationController?.pushViewController(
paymentViewController,
animated: true
)
按钮确实属于 CreditCardCell
属性必须声明在 Cell 类中:
// CreditCardCell.h
@interface CreditCardCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UIButton *makePaymentButton;
@end
XIB 中的连接应从 CreditCardCell 根对象连到按钮,而不是从 File's Owner 连接。
当前代码已经删除该属性
如果新版类中已没有 makePaymentButton,需要在 Connections Inspector 中删除 XIB 残留的旧连接。这类失效连接旁通常会出现黄色警告标志。
只删除代码中的属性还不够。XIB 仍会保存旧的 outlet 名称,并在运行时尝试为它赋值。
如果修改后仍然崩溃
检查 SDK 的文件是否来自同一版本。以下组合都会直接引发这类异常:
- 新版 Objective-C 类搭配旧版 XIB。
- 旧版静态库搭配新版资源文件。
- App 中存在两个同名的
CreditCardCell.xib。 - 主工程复制了一份 SDK 的 XIB,同时 framework 内又包含另一份。
- XIB 连接的是
makePaymentButton,但实际属性已经改名。 - SDK 的类实现没有被链接,导致 XIB 被当作基础 UIKit 对象加载,随后在设置 outlet 时崩溃。
清理这些问题后,执行一次 Clean Build Folder,删除 App 并重新安装,排除构建产物中仍残留旧资源的情况。
正确状态是:运行时能够解析 CreditCardCell,XIB 从正确的 bundle 加载,makePaymentButton 也只连接到真正声明该 IBOutlet 的对象。
备注:内容仅供参考。
