构建不含嵌入CocoaPod依赖的Cocoa Touch Framework问题咨询
问题背景
你把依赖Alamofire、Firebase的SDK打包成Cocoa Touch Framework,用CocoaPods管理依赖,构建和使用都正常,但当集成该框架的App通过CocoaPods引入相同依赖时,出现了类重复定义的错误:
objc[7299]: Class APMPBDynamicFilterResultTimestamp is implemented in both /private/var/containers/Bundle/Application/9966CA12-11F5-42FE-91FF-BB7A91C07571/MyProject.app/Frameworks/MyFrameworkName.framework/MyFrameworkName (0x101b34158) and /var/containers/Bundle/Application/9966CA12-11F5-42FE-91FF-BB7A91C07571/MyProject.app/MyProject (0x1007416e8). One of the two will be used. Which one is undefined.
这是因为你的Framework把依赖库的代码静态嵌入到了自身二进制里,导致和App侧的依赖产生冲突。我们需要调整构建配置,让Framework编译时能找到依赖,但最终产物不包含这些依赖的代码。
解决步骤
1. 调整Podfile,让依赖以动态框架形式引入
在SDK项目的Podfile中,确保开启use_frameworks!,并且不要强制静态链接依赖库。这样依赖会以动态框架的形式存在,不会被打包进你的SDK:
use_frameworks! target 'MyFrameworkName' do # 声明你的依赖,默认会以动态框架引入 pod 'Alamofire' pod 'Firebase/Core' # 如果某些依赖默认是静态库,添加 :modular_headers => true 强制转为动态兼容的模块化形式 # pod 'SomeStaticDependency', :modular_headers => true end
2. 修改SDK项目的Build Settings配置
打开你的Framework项目,进入Build Settings面板:
- 找到
Linked Frameworks and Libraries,添加Alamofire、Firebase等依赖库,然后把每个依赖的Embed选项设置为Do Not Embed(这一步是关键,告诉Xcode只链接不嵌入) - 找到
Embedded Binaries,确保里面只有你自己的MyFrameworkName.framework,移除所有依赖库 - 检查
Other Linker Flags,不要添加-static这类强制静态链接的参数,保留-ObjC(如果你的SDK需要Objective-C兼容)
3. 优化构建脚本,清理旧构建缓存
你的现有构建脚本已经是合并device和simulator的二进制,但建议在构建前清理DerivedData,避免旧的静态编译产物残留:
# 在脚本开头添加清理步骤 set -e FRAMEWORK_NAME="MyFrameworkName" OUTPUT_DIR="${SRCROOT}/framework/build" # 清理旧的构建缓存和DerivedData rm -rf "${OUTPUT_DIR}" xcodebuild clean -workspace "${FRAMEWORK_NAME}.xcworkspace" -scheme "${FRAMEWORK_NAME}" -configuration Release # 后续的构建命令保持不变 xcodebuild -workspace "${FRAMEWORK_NAME}.xcworkspace" -scheme "${FRAMEWORK_NAME}" -configuration Release -arch arm64 -arch armv7 -arch armv7s only_active_arch=no defines_module=yes -sdk "iphoneos" -derivedDataPath "${OUTPUT_DIR}" xcodebuild -workspace "${FRAMEWORK_NAME}.xcworkspace" -scheme "${FRAMEWORK_NAME}" -configuration Release -arch x86_64 -arch i386 only_active_arch=no defines_module=yes -sdk "iphonesimulator" -derivedDataPath "${OUTPUT_DIR}" # ... 剩余脚本内容保持不变
4. 验证最终产物
构建完成后,做两个验证:
- 打开终端,执行
lipo -info "${OUTPUT_DIR}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}",确认输出的架构只有你的SDK目标架构,没有额外的依赖代码 - 查看
MyFrameworkName.framework/Modules目录,里面应该只有你自己的MyFrameworkName.swiftmodule相关文件,没有依赖库的模块文件
这样处理后,你的Framework在编译时会依赖这些库保证编译通过,但最终产物不会包含依赖代码。当集成的App通过CocoaPods引入相同依赖时,App会统一管理这些动态框架,避免类重复定义的冲突。
内容的提问来源于stack exchange,提问作者Jeroen

