WWDC Quick Look 💓 By SwiftGGTeam
Distribute binary frameworks as Swift packages

Distribute binary frameworks as Swift packages

观看原视频

Highlight

Xcode 12 让 Swift Package Manager 可以通过 .binaryTarget 分发预编译的 .xcframework,使用方像添加源码 package 一样声明依赖,发布方用 HTTPS URL 和 checksum 验证下载到的二进制文件。

核心内容

在 Xcode 11 之前,Swift Package Manager 主要解决源码库分发。开源库可以把源码放进 package,使用方在 Xcode 里添加依赖,版本更新和构建都交给工具链处理。闭源 SDK 的体验不同:作者通常只交付编译好的 framework,使用方要手动下载、拖进工程、处理平台切片,还要自己确认版本是否匹配。

Xcode 11 引入的 XCFramework 解决了二进制格式问题。一个 XCFramework 可以把不同平台和 target environment 的 framework 或 library 组合在一起,每个子目录对应一个 target triple。它适合分发闭源库,但还没有进入 Swift package 的依赖解析流程。

Xcode 12 把这两条线接起来。Swift tools version 5.3 新增 binaryTarget,package 作者可以在 Package.swift 里声明一个预编译的 XCFramework。使用方仍然通过 File > Swift Packages > Add Package Dependency 添加仓库和版本,Xcode 会下载二进制产物,校验 checksum,再把模块暴露给 App 代码。

这场 session 重点放在把闭源框架纳入 Swift package 的分发模型。作者得到的是仓库、版本约束、HTTPS artifact 和 checksum 组成的发布协议;使用方得到的是和源码 package 基本一致的集成入口。

详细内容

使用二进制 package 依赖

01:01)演讲先从使用方开始。开发者在 Xcode 里选择 File > Swift Packages > Add Package Dependency,选择 BinaryEmoji package 的 1.0 版本,然后把二进制依赖链接到 App。项目导航里会出现 Referenced Binaries 分组,里面是一个 XCFramework。

02:37)同一个依赖也可以写在 package manifest 里。官方代码片段展示了普通 package dependency 的写法:

// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "package",
    products: [
        .library(
            name: "package",
            targets: ["package"]),
    ],
    dependencies: [
        .package(url: "https://github.com/JohnnyAppleseed2020/BinaryEmoji", from: "1.0.0"),
    ],
    targets: [
        .target(
            name: "package",
            dependencies: ["Emoji"]),
    ]
)

关键点:

  • // swift-tools-version:5.3 表示这个 manifest 使用 Xcode 12 对应的 SwiftPM 能力。
  • .package(url:from:) 指向分发这个二进制 framework 的 package 仓库,并设置最低版本。
  • .target(... dependencies: ["Emoji"]) 让当前 target 可以导入 Emoji 模块。
  • 使用方看不到二进制 framework 的源码,但导入模块、跳转定义、刷新 SwiftUI preview 的入口和源码 package 一样。

用 binaryTarget 分发 XCFramework

03:04)作者侧的关键 API 是新的 target 类型 binaryTarget。它的名字对应 XCFramework 的 module name,远程分发时需要 HTTPS URL 和 checksum。

// swift-tools-version:5.3

import PackageDescription

let package = Package(
    name: "Emoji",
    products: [
        .library(name: "Emoji", targets: ["Emoji"])
    ],
    dependencies: [
    ],
    targets: [
        .binaryTarget(
            name: "Emoji",
            url: "https://example.com/Emoji/Emoji-1.0.0.xcframework.zip",
            checksum: "6d988a1a27418674b4d7c31732f6d60e60734ceb11a0ce9b54d1871918d9c194"
        )
    ]
)

关键点:

  • .library(name:targets:)Emoji target 作为 package 产品暴露给客户端。
  • .binaryTarget(name:url:checksum:) 声明这个 target 来自一个压缩后的 XCFramework。
  • url 必须是 HTTPS,下载动作会独立于 Git checkout,避免把大型二进制文件写进仓库历史。
  • checksum 是 manifest 的完整性约束,Xcode 下载 artifact 后会重新计算并比对。
  • 逐字稿还提到 path-based binary target,它用于开发阶段,可以指向 package 内的 XCFramework 文件。

checksum 是发布协议的一部分

05:43)checksum 由 SwiftPM 命令计算。作者先把 XCFramework 打成 zip,再对这个 zip 运行命令:

swift package compute-checksum Emoji-1.0.0.xcframework.zip

关键点:

  • 命令输出的值要复制到 Package.swiftchecksum 字段。
  • 客户端使用 package 时,Xcode 会对下载文件重新计算 checksum。
  • checksum 不匹配时,Xcode 会拒绝这个下载结果,防止客户端拿到和 manifest 不一致的二进制文件。
  • artifact 文件名里的版本号要和 package 发布节奏配合,逐字稿建议二进制 target 也遵守 semantic versioning。

XCFramework 仍然是底层格式

06:16binaryTarget 分发的是 XCFramework。逐字稿强调,XCFramework 可以包含多个平台变体,支持 framework、dynamic library 和 static library,并且每个 XCFramework 只包含一个 module。

创建 XCFramework 的流程在本场只做概述:在 framework 或 library target 上打开 Build Libraries for Distribution,分别 archive 每个变体,再用 xcodebuild -create-xcframework 合并。演讲者把更完整的创建和版本演进细节指向 WWDC 2019 的 “Binary Frameworks in Swift”。

07:00)最后的取舍也很实际。二进制依赖会让调试更难,使用方不能直接修源码,也只能使用作者已经支持的平台。把闭源 SDK 放进 Swift package 以后,依赖管理更顺,但评估第三方组件的责任没有消失。

核心启发

  • 做什么:把闭源 SDK 改造成 Swift package 分发。为什么值得做:Xcode 12 支持 .binaryTarget,用户可以通过 package dependency 获取预编译 XCFramework。怎么开始:先准备一个只负责分发的 package 仓库,在 Package.swift 中声明 .library 产品和 .binaryTarget(name:url:checksum:)

  • 做什么:为内部团队发布私有二进制组件。为什么值得做:HTTPS artifact 会在 Git checkout 之外下载,大文件不会污染 package 仓库历史。怎么开始:把 zip 后的 XCFramework 放到公司可访问的 HTTPS 地址,计算 checksum,把 package tag 和 artifact 版本对应起来。

  • 做什么:建立二进制 SDK 的升级规则。为什么值得做:逐字稿明确建议二进制 target 像源码 target 一样遵守 semantic versioning,破坏性 API 变更要提升 major version。怎么开始:把 public API 变更、XCFramework bundle version 和 package tag 放进同一套发布检查表。

  • 做什么:给二进制依赖增加集成验收。为什么值得做:使用方无法修改二进制源码,问题更适合在接入边界提前发现。怎么开始:新建一个示例 App 或测试 package,按客户端路径添加 package dependency,验证 import、主要 API 调用和目标平台是否可用。

关联 Session

评论

GitHub Issues · utterances