在 Xcode 中使用 Swift Package

from https://xiaozhuanlan.com/topic/9635421780

目前,在 macOS/iOS 开发中,我们通常使用 CocoaPods 或 Carthage 等非官方工具来管理项目工程中对第三方开源库的依赖。

Swift Package Manager(Swift 包管理器,一般简称 SwiftPM 或者 SPM)是苹果官方提供的一个用于管理源代码分发的工具,旨在使分享代码和复用其他人的代码变得更加容易。该工具可以直接帮助我们编译和链接 Swift packages(包),管理依赖关系、版本控制,以及支持灵活的代码分发和团队协作。

关于 Swift Package Manager 的详细介绍,可以参考去年的 WWDC 2018 Session 411: Getting to Know Swift Package Manager,以及我总结的这篇文章:

WWDC 2018:细说 Swift 包管理工具

SwiftPM 一开始仅支持 macOS 和 Linux 平台上的 Swift 开发,且只能通过命令行的方式来使用。在最新的 Xcode 11 中集成了 libSwiftPM,并提供了图形化操作界面,使 Swift Package 支持 iOS/watchOS/tvOS 等平台。

下文我们将介绍如何在 Xcode 11 中使用 Swift Package 引入第三方开源库或私有代码库。

1. 如何添加 Package

我们先通过 Xcode 11 新建一个 Swift iOS 工程,假设名为 MyTestProject,然后可以通过如下两种方式添加 Swift Package 依赖:

Xcode Menu -> File -> Swift Packages -> Add Package Dependency...

在 Xcode 工程中选中当前 Project 名称 -> 选择 Swift Packages -> 点击 + 图标添加

在弹出的窗口中,我们可以输入要依赖的 Package 的 git 仓库地址,例如,我们这里要在工程中添加 Yams 这个 Swift YAML 文件解析的开源库,则可以在输入框中填写其在 GitHub 上的 git url,如下图所示:

此外,我们可以在 Xcode -> Preferences... -> Accounts 中添加并登录自己的 GitHub/GitLab账号或者公司内部私有 Git 服务器账号,然后就可以在 Choose Package Repository 窗口中直接选择你自己的或者已关注的 Swift Package,如下图:

选择好要依赖的 Package 后,点击 Next 按钮进行版本号设置。我们可以指定 Package 的版本号范围,规则如下,与 CocoaPods 类似:

Up to Next Major: 当前指定的版本号到下一个大版本号之间的最新版本,例如 2.0.0 ~ 3.0.0(不包含 3.0.0)

Up to Next Minor: 当前指定的版本号到下一个次版本号之间的最新版本,例如 2.0.0 ~ 2.1.0(不包含 2.1.0)

Range: 指定的两个版本号之间的最新版本,例如 2.1.0 ~ 2.7.2(不包含 2.7.2)

Exact: 指定使用某一具体的版本号

同时,我们也可以指定要依赖当前 Package git 仓库的某一个分支或者某一次 commit。

最后,勾选当前 Package 要添加到工程中的哪些 Targets,即可。

添加好 Package 之后,我们就可以在 Xcode 工程中查看到相关信息,如下图:

接下来我们就可以在代码中 import Yams 然后调用它的相关 API 了。

2. Package 概览

下面我们介绍一下 Swift Package 的内部结构。

一个 Package(包)由 Swift 源码文件和一个清单文件组成。这个清单文件被命名为 Package.swift,它使用 PackageDescription 模块来定义包的名称、内容以及依赖关系。

以 Yams 为例,它包含的内容如下:

Package.swift: 包的清单文件,用于描述包的名称、内容、依赖关系、支持的 Swift 版本号;

Sources: 源码文件夹,通常包括 C/C++ 代码和 Swift 代码等;

Tests: 单元测试代码

而 Package.swift 文件的大致内容如下:

另外,如果一个 Package 依赖了另一个 Package,也需要在 Package.swift 文件中进行声明。例如,有一个 Package 叫 "DesignTheme",它依赖了 "DesignFont",则需要在 "DesignTheme" 的 Package.swift 文件中添加如下依赖代码:

dependencies: [

.package(url:"http://github.com/WWDC19/DesignFont.git"),

]

此时,当在 Xcode 工程的 Swift Packages 中添加了 "DesignTheme",同时就会自动下载依赖 "DesignFont",不需要我们再手动添加了。

因此,对于一个 Xcode 工程,当其依赖了一些 Swift Packages 后,在编译链接时,SwiftPM 会自动编译每个 Package 并链接到主可执行文件中。

PS: 对于 SwiftPM 的一些基本概念,例如:Modules、Packages、Products、Dependencies、Targets 等,在 Swift.org 官网已经有非常详细的描述和定义,另外,也可以参见我之前写的这篇文章,这里不再赘述。

3. Package 依赖详解

本节我们详细介绍一下 Swift Package 之间互相依赖的一些细节。

如前面所述,假如我们在 Demo 工程(名为 "Lunch")中,除了引入 "Yams" Package 外,又引入了一个 "DesignTheme" Package,如下图:

此时,如果 "DesignTheme" Package 又同时依赖 "DesignFont" 和 "DesignColor" 两个库,我们就会在 Xcode 工程的 Package 依赖列表中看到自动引入了这两个依赖的 Package:

那 Xcode 是如何找到他们的依赖关系和版本号信息呢?

正如前面所说的,其实在 "DesignTheme" 的 Package.swift 文件中有详细描述这些依赖关系,然后 Xcode 会根据该文件的信息来选择下载对应 Package 的最佳版本号:

另外一点需要注意的是,我们在上述 Demo 工程的 Swift Packages 中,仅添加了对 “Yams” 和 "DesignTheme" 两个 Packages 的依赖,所以我们可以在工程代码中直接 import Yams 和 import DesignTheme 然后调用它们相关的 API:

而另外两个 Packages "DesignFont" 和 "DesignColor" 是由 "DesignTheme" 的依赖隐式引入的,所以我们不能在代码中直接 import 使用它们。

假如 Demo 工程中需要用到 "DesignFont" 相关的 API,则需要在 Xcode 工程的 Swift Packages 列表中显式地添加依赖才行。

4. 如何更新 Package

当我们的 Xcode 工程添加了一些 Swift Package 后,如果其中的一些 Package 有了新版本,例如,上述 "DesignFont" 我们一开始依赖的是 1.2.0,后面其修复了 bug 并发布了新版本 1.2.1,我们该如何更新呢?

其实非常方便,我们可以在 Xcode Menu 中点击 File -> Swift Packages,然后选择 "Update to Latest Package Versions" 即可,如下图所示,此时当前工程中所有的 Packages 都会自动更新到指定版本范围内的最新版本

那么,Xcode 更新一个 Package 时,除了下载更新最新的代码外,还做了哪些事情呢?

其实,Xcode 维护了一个 Package.resolved 文件用于记录当前工程已添加的各个 Packages 的详细信息,包含名称、URL、分支和具体的版本号等信息,位于 .xcodeproj 里,路径为:

Package.resolved 是一个 JSON 文件,内容大致如下:

{

"object": {

"pins": [

      {

"package":"Yams",

"repositoryURL":"git@github.com:jpsim/Yams.git",

"state": {

"branch":null,

"revision":"c947a306d2e80ecb2c0859047b35c73b8e1ca27f",

"version":"2.0.0"

        }

      },

      // other packages

    ]

  },

"version":1

}

当某一个 Package 更新时,上述文件中该 Package 对应的 revision 和 version 就会被修改为最新信息,如下图所示,"DesignFont" 从 1.2.0 更新到 1.2.1:

所以,与 CocoaPods 的 Podfile.lock 和 Carthage 的 Cartfile.resolved 文件作用类似,当一个工程涉及到多人协作开发时,我们就应该在 git 仓库中提交 Package.resolved 文件,保证团队中每个人用的 Packages 都是同一个版本,避免带来不必要的问题。

5. 解决 Package 版本冲突

仍然以第三节举的 Demo 工程为例,其中,我们依赖的 "DesignTheme" 的版本号设置为 1.0.0 - Next Major,而 "DesignTheme" 的 Package.swift 文件中声明的两个依赖 "DesignFont" 和 "DesignColor" 对应的版本号也为 1.0.0 - Next Major,此时,这 3 个 Packages 在 Xcode 中最终被引入的版本号如下:

由于 "DesignTheme" 已经隐式引入 1.2.0 版本的 "DesignFont" 了,这个时候,如果我们要在 Demo 工程的 Swift Packages 中直接显式地引入 "DesignFont" 的 2.0.0 以上版本,就会报错,存在版本冲突。

这是因为,在一个 Xcode 工程里(workspace),对于同一个 Package,只能引入一个版本号。

那么如何解决这个问题呢?

只能是更新 "DesignTheme" 到 2.0.0,并在其 Package.swift 文件中声明依赖的 "DesignFont" 的版本号也从 2.0.0 起。然后,我们再在 Xcode 工程的 Swift Packages 列表中显式地添加 2.0.0 起的 "DesignFont"。

6. 如何创建 Packages

本文主要讲解如何在最新的 Xcode 11 中使用 Swift Package,如果你想学习如何创建一个 Package 用于在 GitHub 上开源或者团队内部使用,可以阅读以下两个相关 Session:

WWDC 2019 Session 410: Creating Swift Packages

WWDC 2018 Session 411: Getting to Know Swift Package Manager

7. 结语

在 GitHub 上,绝大部分 Swift 开源库都已经支持通过 SwiftPM 来接入,所以如果你的 Xcode 工程之前有使用一些第三方的 Swift 库,不妨改成用 Xcode 11 自带的方式来导入试试吧。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容