带SPM依赖的XCFramework存在依赖暴露异常与重复定义问题
问题背景
我有一个作为Swift包管理器(SPM)包的库ExampleLibrary,它依赖第三方库CryptoSwift,其Package.swift配置如下:
// swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "ExampleLibrary", platforms: [ .macOS(.v10_15), .iOS(.v13), .tvOS(.v13) ], products: [ // Products define the executables and libraries a package produces, making them visible to other packages. .library( name: "ExampleLibrary", type: .dynamic, targets: ["ExampleLibrary"]), ], dependencies: [ .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMajor(from: "1.8.1")), ], targets: [ // Targets are the basic building blocks of a package, defining a module or a test suite. // Targets can depend on other targets in this package and products from dependencies. .target( name: "ExampleLibrary", dependencies: ["CryptoSwift"]), .testTarget( name: "ExampleLibraryTests", dependencies: ["ExampleLibrary"]), ] )
将该库编译导出为二进制XCFramework后,我将其作为另一个SPM包ExampleSDK进行分发,配置如下:
// swift-tools-version: 5.9 import PackageDescription let package = Package( name: "ExampleSDK", platforms: [ .iOS(.v13), .macOS(.v10_15), ], products: [ .library( name: "ExampleSDK", targets: ["ExampleLibraryFramework"]), ], targets: [ .binaryTarget(name: "ExampleLibraryFramework", path: "ExampleLibrary.xcframework") ] )
当我将ExampleSDK导入到App项目中并使用:
import ExampleLibrary
此时会报错:Missing required module 'CryptoSwift'。
若我在App项目中单独导入CryptoSwift作为依赖,则会出现重复定义错误:
objc[5702]: Class _TtC11CryptoSwift15XChaCha20Worker is implemented in both [...]/ExampleLibrary (0x1016edcf0) and [...]/example.app/example (0x100611908). One of the two will be used. Which one is undefined.
即使我在ExampleSDK的配置中添加CryptoSwift依赖(如下所示),虽然不再提示缺失依赖,但重复定义问题依然存在:
// swift-tools-version: 5.9 import PackageDescription let package = Package( name: "ExampleSDK", platforms: [ .iOS(.v13), .macOS(.v10_15), ], products: [ .library( name: "ExampleSDK", targets: ["ExampleLibraryFramework", "ExampleSDK"]), ], dependencies: [ .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", .upToNextMajor(from: "1.8.1")), ], targets: [ .target(name: "ExampleSDK", dependencies: ["CryptoSwift"]), .binaryTarget(name: "ExampleLibraryFramework", path: "ExampleLibrary.xcframework") ] )
我尝试过在ExampleLibrary中使用@_implementationOnly import CryptoSwift来隐藏依赖,但无法阻止App导入CryptoSwift时出现重复问题。
现提出两个问题:
- 如何阻止
ExampleLibrary暴露CryptoSwift,使App导入时不会出现重复定义问题? - 如何让App项目识别到该依赖已嵌入在XCFramework中?
(XCFramework内部结构:
)
解决方案
问题1:阻止ExampleLibrary暴露CryptoSwift
要彻底隐藏CryptoSwift依赖,需从编译阶段和包配置双管齐下:
- 规范
@_implementationOnly使用并清理公开接口
在ExampleLibrary的所有源文件中,将普通import CryptoSwift替换为@_implementationOnly import CryptoSwift。同时检查库的公开接口文件(.swiftinterface),确保没有任何CryptoSwift的类型、协议或符号被暴露——如果库的公开API直接使用了CryptoSwift的类型(比如函数参数/返回值),必须封装成自身库的内部类型,避免对外透传第三方依赖。 - 编译XCFramework时静态链接
CryptoSwift
修改ExampleLibrary的Package.swift,强制静态链接CryptoSwift,让第三方库的符号直接嵌入到ExampleLibrary的二进制中,而非作为动态依赖存在:
之后使用.target( name: "ExampleLibrary", dependencies: [ .product(name: "CryptoSwift", package: "CryptoSwift", type: .static) ] )swift package create-xcframework命令编译XCFramework,确保构建过程中依赖被静态嵌入。
问题2:让App项目识别依赖已嵌入XCFramework
需在ExampleSDK的Package.swift中正确配置二进制目标的链接规则,告诉SPM无需额外引入CryptoSwift:
// swift-tools-version: 5.9 import PackageDescription let package = Package( name: "ExampleSDK", platforms: [ .iOS(.v13), .macOS(.v10_15), ], products: [ .library( name: "ExampleSDK", targets: ["ExampleLibraryFramework"]), ], targets: [ .binaryTarget( name: "ExampleLibraryFramework", path: "ExampleLibrary.xcframework", linkerSettings: [ // 告知链接器无需额外链接CryptoSwift .linkedLibrary("CryptoSwift", .when(platforms: [.iOS, .macOS])), // 避免因未找到外部依赖报错 .unsafeFlags(["-Xlinker", "-undefined", "dynamic_lookup"]) ], // 标记该二进制目标不对外暴露依赖 excludeDependencies: ["CryptoSwift"] ) ] )
同时可以通过以下方式验证:
- 检查XCFramework中各平台二进制文件的
Info.plist,确认没有包含CryptoSwift的依赖信息; - 使用
otool -L命令查看二进制文件,确认CryptoSwift的符号已被静态嵌入,而非动态依赖项。
额外注意事项
- 如果
CryptoSwift本身默认是动态库,需要先将其编译为静态库再链接,可通过修改CryptoSwift的Package.swift或构建时传递--static-swift-stdlib参数实现; - 确保App项目不会单独引入
CryptoSwift,依赖的唯一来源应为ExampleLibrary的XCFramework。
内容的提问来源于Stack Exchange,提问作者Paweł Madej
相关产品推荐
相关产品推荐

