Swift组件:使用第三方静态库
最近在做的项目,使用了组件化方案,组件都是用Swift编写。在做用户组件的时候,用到了微信登录SDK,该SDK是一个使用OC编写的静态库,文件只有.a和.h文件,通过CocoaPods引入到项目后,尝试导入,结果报错找不到该库。
为什么找不到静态库?
明明导入了SDK,为什么找不到?
很简单,因为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
:
framework module MJRefresh {
umbrella header "MJRefresh-umbrella.h"
export *
module * { export * }
}
这一套下来,Swift就知道可以导入哪个OC库,里边有哪些OC类可以用。
但是,为什么导入微信提供的开源库之后,没有自动生成上面说的这些文件?
因为微信开源库提供的文件不是源码,而是打包好的静态库和头文件。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'
3、在Swift文件中导入头文件
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 项目)。