文章目录

  1. 了解二进制在 Swift 中的演化
  2. 指令行东西相关
  3. 定论

iOSmacOS 开发中, Swift 包现在变得越来越重要。Apple 现已尽力推进桥接那些缝隙,而且修复那些阻止开发者的问题,例如阻止开发者将他们的库和依靠由其他比如 CarthageCocoaPods 依靠管理东西迁移到 Swift 包依靠管理东西的问题,例如没有才能增加构建步骤的问题。这对任何依靠一些代码生成的库来说都是破坏者,比如,协议和 Swift 生成。

了解二进制在 Swift 中的演化

为了充沛了解 Apple 的 Swift 团队在二进制方针和他们引进的一些新 API 方面采取的一些步骤,咱们需求了解它们从何而来。在后续的部分中,咱们将调研 Apple 架构的演化,以及为什么二进制方针的 API 在过去几年中逐渐构成的,特别是自 Apple 发布了自己的硅芯片之后。

胖二进制和 Frameworks 结构

假如你曾有必要处理二进制依靠,或许你曾创立一个归于你自己的可履行文件,你将会对 胖二进制 这个术语感到了解。这些被扩展(或增大)的可履行文件,是包括了为多个不同架构原生构建的切片。这允许库的一切者分发一个运转在一切预期的方针架构上的独自的二进制。

当源码不能被暴露或当处理十分庞大的代码仓库时,预编译库成为可履行文件十分有意义,由于预编译源码以及以二进制文件分发他们,将节约构建程序在他们的应用上的构建时间。

Pods 是一个十分好的比如,当开发者发现他们自己没必要构建那些十分少改动的依靠。这是一个很共通的问题,它激发了比如 cocoapods-binary 之类的项目,该项目预编译了 pod 依靠项以减少客户端的构建时间。

Frameworks 结构

嵌入静态二进制文件或许对应用程序来说现已足够了,但假如需求某些资源(如 assets 或头文件),则需求将这些资源与包括一切切片的 胖二进制文件 绑缚在一起,构成所谓的 frameworks 文件。

这就是比如 Google Cast 之类的预编译库在过渡到运用 xcframework 进行分发之前所做的工作 —— 下一节将详细介绍这种过渡的原因。

到目前为止,一切都很好。 假如咱们要为分发预编译一个库,那么胖二进制文件听起来很理想,对吧?而且,假如咱们需求绑缚一些其他资源,咱们能够只运用一个 frameworks。 一个二进制来统治他们一切!

XCFrameworks 结构

好吧,不完全是。胖二进制文件有一个大问题,那就是你不能有两个架构相同但指令/指令不同的切片。 这从前很好,由于设备和模拟器的架构总是不同的,但是随着 Apple Silicon 计算机 (M1) 的推出,模拟器和设备同享相同的架构 (arm64),但具有不同的加载器指令。 这与面向未来的二进制方针相结合,正是 Apple 引进 XCFrameworks 的原因。

你能够在 Bogo Giertler 编撰的这篇精彩文章 中详细了解为 iOS 设备构建的 arm64 切片和为 M1 mac 的 iOS 模拟器构建的 arm64 切片之间的差异。

XCFrameworks 现在允许将多个二进制文件绑缚在一起,解决了 M1 Mac 引进的设备和模拟器冲突架构问题,由于咱们现在能够为每个用例提供包括相关切片的二进制文件。 事实上,假如咱们需求,咱们能够走得更远,例如,在同一个 xcframework 中绑缚一个包括 iOS 方针的 UIKit 接口的二进制文件和一个包括 macOS 的 AppKit 接口的二进制文件,然后让 Xcode 根据希望的方针架构决议运用哪一个。

在 Swift 包中,那先能够以 binaryTarget 被包括进项意图,能够在包中被引进任意其他方针。这相同的操作同样适用于 frameworks

指令行东西相关

由于 Swift 5.6 版别中引进了用于 Swift 包管理器的 可扩展构建东西 ,因此能够在构建过程中的不一起间履行指令。

这是 iOS 社区长期以来一直强烈要求的工作,例如格式化源代码、代码生成甚至搜集公制代码库的指标。 Swift 5.6 中一切这些所谓的 插件 最终都需求调用可履行文件来履行特定任务。 这是二进制文件再次在 Swift 包中参加的当地。

在大多数情况下,关于咱们 iOS 开发人员来说,这些东西将来自一起支撑 macOS 的不同架构切片 —— Apple Silicon 的 arm64 架构和 Intel Mac 的 x86_64 架构。开发者东西如, SwiftLint 或 SwiftGen 正是这种事例。 在这种情况下,能够运用包括可履行文件(本地或远程)的 .zip 文件的途径创立新的二进制方针。

注意可履行文件有必要在.zip文件的根目录下,不然找不到。

Artifact Bundles

到目前为止,指令行东西所选用的办法仅适用于 macOS 架构。但咱们不能忘记,Linux 机器也支撑 Swift 包。 这意味着假如要一起支撑 M1 macs (arm64) 和 Linux arm64 机器,上面的胖二进制办法将不起作用 —— 请记住,二进制不能包括具有相同架构的多个切片。 在这个阶段或许有人会想,咱们能够不只运用 xcframeworks 吗? 不,由于它们在 Linux 操作系统上不受支撑!

Apple 现已考虑到这一点,除了引进 可扩展构建东西 之外,Artifact Bundles 和对二进制方针的其他改进也作为 Swift 5.6 的一部分发布。

工件包(Artifact Bundles) 是包括 工件 的目录。 这些工件需求包括支撑架构的一切不同二进制文件。 二进制文件和支撑的架构的途径是运用清单文件 (info.json) 指定的,该文件位于 Artifact Bundle 目录的根目录中。 你能够将此清单文件视为一个地图或指南,以帮助 Swift 确认哪些可履行文件可用于哪种架构以及能够在哪里找到它们。

以 SwiftLint 为例

SwiftLint 在整个社区中被广泛用作 Swift 代码的静态代码剖析东西。 由于很多人都十分巴望让这个插件在他们的 SwiftPM 项目中运转,我以为这将是一个很好的比如来展现咱们如何将分发的可履行文件从他们的发布页面变成一个与 macOS 架构和 Linux arm64 兼容的工件包。

让咱们从下载两个可履行文件(macOS 和 Linux)开始。

至此,bundle的结构就能够创立好了。 为此,创立一个名为 swiftlint.artifactbundle 的目录并在其根目录增加一个空的 info.json

mkdir swiftlint.artifactbundle
touch swiftlint.artifactbundle/info.json

现在能够运用 schemaVersion 填充清单文件,这或许会在未来版别的工件包和具有两个变体的工件中发生变化,这将很快界说:

{
    "schemaVersion": "1.0",
    "artifacts": {
        "swiftlint": {
            "version": "0.47.0", # The version of SwiftLint being used
            "type": "executable",
            "variants": [
            ]
        },
    }
}

需求做的最后一件事是将二进制文件增加到包中,然后将它们作为变体增加到 info.json 文件中。 让咱们首要创立目录并将二进制文件放入其中(macOS 的一个在 swiftlint-macos/swiftlint,Linux 的一个在 swiftlint-linux/swiftlint)。

增加这些之后,能够在清单文件中变量

{
    "schemaVersion": "1.0",
    "artifacts": {
        "swiftlint": {
            "version": "0.47.0", # The version of SwiftLint being used
            "type": "executable",
            "variants": [
			          {
                    "path": "swiftlint-macos/swiftlint",
                    "supportedTriples": ["x86_64-apple-macosx", "arm64-apple-macosx"]
                },
	              {
                    "path": "swiftlint-linux/swiftlint",
                    "supportedTriples": ["x86_64-unknown-linux-gnu"]
                },
            ]
        },
    }
}

为此,需求为每个变量指定二进制文件的相对途径(从工件包目录的根目录)和支撑的三元组。 假如您不了解 方针三元组,它们是一种选择构建二进制文件的架构的办法。 请注意,这不是 主机(构建可履行文件的机器)的体系结构,而是 方针 机器(应该运转所述可履行文件的机器)。

这些三元组具有以下格式: ---- 并非一切字段都是必需的,假如其中一个字段未知而且要运用默认值,则能够省略或替换为 unknown 关键字。

可履行文件的架构切片能够经过运转 file 找到,这将打印绑缚的任何切片的供货商、系统和架构。 在这种情况下,为这两个指令运转它会显现:

swiftlint-macos/swiftlint

swiftlint: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64]
swiftlint (for architecture x86_64):	Mach-O 64-bit executable x86_64
swiftlint (for architecture arm64):	Mach-O 64-bit executable arm64

swiftlint-linux/swiftlint

-> file swiftlint
swiftlint: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, with debug_info, not stripped

这带来了上面显现的 macOS 支撑的两个三元组(x86_64-apple-macosxarm64-apple-macosx)和 Linux 支撑的一个三元组(x86_64-unknown-linux-gnu)。

XCFrameworks 相似,工件包也能够经过运用 binaryTarget 包括在 Swift 包中。

定论

简而言之,咱们能够总结 2022 年如何在 Swift 包中运用二进制文件的最佳实践,如下所示:

  1. 假如你需求为你的 iOS/macOS 项目增加预编译库或可履行文件,您应该运用 XCFramework,并为每个用例(iOS 设备、macOS 设备和 iOS 模拟器)包括独自的二进制文件。
  2. 假如你需求创立一个插件并运转一个可履行文件,你应该将其嵌入为一个工件包,其中包括适用于不同支撑架构的二进制文件。

我正在参加技术社区创作者签约方案招募活动,点击链接报名投稿。