以 Swift Package 形式分发二进制框架
Distribute binary frameworks as Swift packages
2020年6月24日
一句话判断
Swift Package Manager 终于支持二进制分发——如果你还在用 CocoaPods 管理闭源 SDK,是时候认真考虑迁移了。
这场 Session 讲了什么
Swift Package Manager 在 Xcode 12 中引入了对二进制框架(binary targets)的原生支持。这意味着你可以在 Swift Package 中直接引用预编译的 .xcframework 文件,而不再要求所有依赖都必须是源码形式。Session 详细介绍了 BinaryTarget 的配置方式,支持从本地路径或远程 ZIP 归档引用二进制文件。
这个特性的核心价值在于弥合了开源社区和商业 SDK 之间的分发鸿沟。以前,闭源 SDK 只能通过 CocoaPods 或手动集成的方式分发,现在它们可以像普通 Swift Package 一样被声明为依赖。用户只需要在 Package.swift 中添加一行依赖声明,Xcode 就会自动下载并集成。
Session 还讨论了 XCFramework 的定位——它是 Apple 推荐的二进制框架格式,支持多平台、多架构的 fat binary 分发。结合新的 BinaryTarget API,整个分发流程变得非常顺畅:开发者用 xcodebuild -create-xcframework 打包好 XCFramework,上传到服务器或 GitHub Release,然后在 Package.swift 中通过 URL 引用即可。
值得深挖的点
Binary Target 与常规 Target 的混合使用
一个 Package 可以同时包含源码 target 和二进制 target。这为 SDK 厂商提供了极大的灵活性:核心引擎以二进制形式分发,而外围的 Swift wrapper 可以保持源码开放,方便用户定制和调试。在 Package.swift 中,你只需在 targets 数组中混合使用 .target() 和 .binaryTarget() 即可。
XCFramework 的架构支持策略
在 Apple Silicon 过渡期,XCFramework 需要同时包含 arm64 (设备)、arm64 (macOS) 和 x86_64 架构。Session 强调了为每个平台-架构组合提供独立 slice 的重要性,因为 Xcode 会根据目标平台自动选择正确的 slice。这意味着一个 iOS XCFramework 需要包含 arm64 (device) 和 x86_64/arm64 (simulator) 两个 slice。
代码片段
// Package.swift 中声明二进制 target
// 方式一:通过本地路径引用
let package = Package(
name: "MySDK",
products: [
.library(name: "MySDK", targets: ["MySDK"]),
],
targets: [
// 二进制 target,指向本地 XCFramework
.binaryTarget(
name: "MyCoreEngine",
path: "Frameworks/MyCoreEngine.xcframework"
),
// 源码 target,依赖二进制框架
.target(
name: "MySDK",
dependencies: ["MyCoreEngine"],
path: "Sources/MySDK"
),
]
)
// 方式二:通过远程 URL 引用二进制框架
let package = Package(
name: "AnalyticsSDK",
products: [
.library(name: "AnalyticsSDK", targets: ["AnalyticsSDK"]),
],
targets: [
.binaryTarget(
name: "AnalyticsSDK",
url: "https://github.com/example/AnalyticsSDK/releases/download/2.0.0/AnalyticsSDK.xcframework.zip",
// 必须提供校验和以确保完整性
checksum: "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
),
]
)
# 创建支持多平台的 XCFramework
# 先为每个平台编译 framework
xcodebuild archive \
-scheme "MyFramework" \
-archivePath "build/ios" \
-sdk iphoneos \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
xcodebuild archive \
-scheme "MyFramework" \
-archivePath "build/ios-simulator" \
-sdk iphonesimulator \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
# 合并为 XCFramework
xcodebuild -create-xcframework \
-framework "build/ios.xcarchive/Products/Library/Frameworks/MyFramework.framework" \
-framework "build/ios-simulator.xcarchive/Products/Library/Frameworks/MyFramework.framework" \
-output "build/MyFramework.xcframework"
最佳实践
- 编译 XCFramework 时务必设置
BUILD_LIBRARY_FOR_DISTRIBUTION=YES,这会生成 Swift 接口文件(.swiftinterface),确保 ABI 稳定性 - 远程二进制的 URL 必须是 HTTPS,且必须提供 SHA-256 checksum 以保证下载完整性
- 使用 GitHub Release 托管 XCFramework ZIP 文件是最简单的方案,版本号与 Git tag 对齐
- 在 CI 中自动化 XCFramework 的打包和发布流程,避免手动操作出错
- 为二进制 target 编写单独的测试 target,虽然看不到源码但可以验证公共 API 的正确行为
还有什么值得关注
- Session 提到 checksum 的计算方式:
swift package compute-checksum <path-to-zip>,这个命令很实用 - 二进制 target 不支持
path和url同时指定,只能二选一 - 如果你的 Package 包含二进制 target,它仍然可以作为其他 Package 的依赖正常工作,SwiftPM 会处理传递性依赖