最近在做的项目,使用了组件化方案,组件都是用Swift编写。在做用户组件的时候,用到了微信登录SDK,该SDK是一个使用OC编写的静态库,文件只有.a和.h文件,通过CocoaPods引入到项目后,尝试导入,结果报错找不到该库。
为什么找不到静态库❓
因为OC没有暴露头文件给Swift。
在常规Swift项目中,创建OC文件时Xcode会自动帮我们创建桥接文件Xx-Bridging-Header.h,供Swift使用OC代码,同时也会创建Xx-Swift.h文件,供OC使用Swift代码,以达到混合开发的目的。
但是,组件是不允许使用桥接文件的。
没有桥接文件怎么办❓
我们来看下Cocoapods的做法。 比如在Swift组件中导入OC库MJRefresh。 导入后会看到在该库的Support Files文件夹中多了MJRefresh-umbrella.h、MJRefresh.modulemap等桥接文件。
其中MJRefresh-umbrella.h文件导入了所有OC头文件:
#import "MJRefreshAutoFooter.h"
#import "MJRefreshBackFooter.h"
#import "MJRefreshComponent.h"
...
MJRefresh.modulemap文件声明了Swift可以导入的库名MJRefresh以及头文件MJRefresh-umbrella.h:
framework module MJRefresh {
umbrella header "MJRefresh-umbrella.h"
export *
module * { export * }
}
这一套下来,Swift就知道可以导入哪个OC库,里边有哪些OC类可以用。
问题来了🤔为什么CocoaPods导入微信开源库没有自动生成上面说的这些文件?
因为微信开源库提供的文件不是源码,而是打包好的静态库,CocoaPods不会为静态库创建这些文件。
我们要想办法让它创建桥接文件。
怎样做才能让CocoaPods生成桥接文件❓
其中一个思路是把SDK包装成一个本地组件!
我们看下微信开源库有哪些文件:
libWechatOpenSDK.a
WechatAuthSDK.h
WXApi.h
WXApiObject.h
第一步,创建文件夹和配置文件,目录如下:
WechatOpenSDK
- WechatOpenSDK
- libWechatOpenSDK.a
- WechatAuthSDK.h
- WXApi.h
- WXApiObject.h
- WechatOpenSDK.podspec
第二步,填写配置内容:
Pod::Spec.new do |s|
s.name = 'WechatOpenSDK'
s.version = '2.0.4'
s.summary = '本地集成的微信 OpenSDK'
s.homepage = 'https://open.weixin.qq.com'
s.license = { :type => 'Commercial' }
s.author = { 'WeChat' => 'wechat@tencent.com' }
s.source = { :path => '.' }
s.ios.deployment_target = '9.0'
s.swift_version = '5.0'
s.requires_arc = true
# 声明是静态库
s.static_framework = true
s.source_files = 'WechatOpenSDK/*'
s.vendored_libraries = 'WechatOpenSDK/*.a'
# SDK依赖
s.libraries = 'c++'}
end
注意:s.source_files需把.a文件也包括在内,否则CocoaPods不会生成桥接文件。
到这,本地组件就做好了。
本地组件怎么用❓
1、在配置文件中依赖该组件:
s.dependency 'WechatOpenSDK'
2、在Podfile文件中指定该组件的路径:
pod 'WechatOpenSDK', :path => '../WechatOpenSDK'
然后执行pod install。
仔细看WechatOpenSDK组件的Support Files文件夹中多出很多文件,包括WechatOpenSDK-umbrella.h和WechatOpenSDK.modulemap,至此,我们成功把OC静态库暴露给Swift🏅。
接下来导入WechatOpenSDK模块即可使用:
import WechatOpenSDK
其他
🧐问题一:组件为什么不能使用桥接文件?
- Bridging-Header 只对主 Target 有效
Bridging-Header 是通过主 App 的 Build Settings 中 Objective-C Bridging Header 设置路径生效的。 这个设置不会自动传递给子模块(比如 Pod 或 Framework)。
- Swift 模块之间是隔离的
Swift Framework 被设计为 模块化的独立单元,不能通过 Bridging-Header 暴露 OC 给 Swift。 如果允许使用 Bridging-Header,模块之间的隔离性就被破坏了,会出现冲突、依赖混乱等问题。
🧐问题二:为什么 CocoaPods 对静态库没有自动生成 umbrella header (umbrella.h) 和 module.modulemap 文件?
Umbrella Header 和 modulemap 是为动态 Framework(modular framework)服务的,而不是为传统的静态库(.a)服务的。
- 静态库(Static Library)本身不支持模块化特性
静态库(.a 文件)是预编译的二进制文件,不具备模块边界、命名空间,本质上只能通过头文件路径手动 #import 来暴露接口。所以CocoaPods 默认不会为 .a 文件创建模块相关的 umbrella header 或 modulemap。
- CocoaPods 默认是非模块化集成方式
CocoaPods 默认行为(不启用 use_frameworks! 或 modular_headers):不开启模块化编译,不生成 module.modulemap,不要求每个 Pod 是一个独立模块。这样做的原因是为了兼容所有 Objective-C 传统项目(非 Swift 项目)。
🧐问题三:为什么这样配置组件后CocoaPods就会生成桥接文件?
因为:
- s.source_files 包含了 .h 文件,CocoaPods 知道要暴露公共头文件。
- 没有禁用 modular headers,CocoaPods 默认生成 module 支持。
- s.static_framework = true 或 use_frameworks!,CocoaPods 会以 framework 方式组织代码。
- Pod 是给 Swift 使用,CocoaPods 会自动生成 module.modulemap 和 umbrella.h 以供 Swift import 使用。