首页 > 代码库 > Swift 包管理器教程
Swift 包管理器教程
原文:An Introduction to the Swift Package Manager
作者: Mikael Konutgan
译者:kmyhy
Swift 包管理器的正式发布是随着 Swift3.0 一起发布的,它是一个用于构建能够运行在 macOS 和 Linux 上的 Swift 库和 app 的新方法。它能够帮助你管理依赖,让你轻松构建、测试和运行你的 Swift 代码。
Swift 包管理器有助于极大地改进 Swift 生态系统,让 Swift 更容易使用、部署到没有 Xcode 的平台上,比如 Linux。Swift 包管理器还能解决在使用多个相互依赖的库时的“依赖地狱”的问题。
重要的一点是,因为 Swift 3 的原因,Swift 包管理器只能在 host 平台上编译。换句话说,你无法编译、使用 iOS、watchOS 和 tvOS 上的包。
让我们开始吧!
开始
在开始之前,确认你已经安装了 Swift 3.0 以上。Swift 3 内置在 Xcode8+,因此如果你已经安装了 Xcode8 或以上,你就可以开始本教程。当然实际上你并不需要用 Xcode 去完成本教程,你可以直接从 swift.org 下载安装 Swift 3。
打开终端窗口,输入 swift package。你会看到这个命令的介绍。你将使用这几个命令:
- swift package init ,创建新包
- swift package update ,更新一个包的依赖
- swift package generate-xcodeproj ,为包生成 Xcode 项目
要学习 Swift 包管理器,我们将编写一个命令行 app,用一个库打印出所有国家的国旗字符。首先我们会创建一个可执行包。这种包也就是命令行 app。Swift web app 也属于这种类别。
通过以下命令,创建一个名为 Flag 的可执行包:
mkdir Flag
cd Flag
swift package init --type executable
在运行 swift package init 命令行时,切换到 Flag 目录是关键的,因为 Flag 将作为包名。你会看到输出中会显示一些文件和文件夹,它们是自动生成的。现在来熟悉一下项目结构:
https://koenig-media.raywenderlich.com/uploads/2016/12/Flag-%E2%94%9C%E2%94%80%E2%94%80-Package.swi_.png’ width= ‘300’/>
- Package.swift 包含包的描述信息,还包含包的依赖。
- Sources/,如名称所示,用于存放你的 Swift 源文件。里面会生成一个 main.swift 文件。这是应用程序的入口。现在,它只会打印 hello world。
- Tests/ ,包含单元测试,你可以用 XCText 编写这些测试。待会你会写测试代码。
回到终端窗口,运行:
swift build
这会编译包并在 .build/debug/ 创建一个可执行文件 Flag。运行这个 app,只需要:
.build/debug/Flag
你会在屏幕上看到 Hello,world!
恭喜你!你创建和编译通过了你的第一个 Swift 包!
创建库
要真正能够生成某个国家的国企字符,我们需要编写一个名为 Atlas 的库。然后在你的 Flag app 中调用这个库。
切到 Flag 包之外的目录,通过终端命令创建一个库:
cd ..
mkdir Atlas
cd Atlas
swift package init --type library
包管理器会创建几个文件和文件夹。
https://koenig-media.raywenderlich.com/uploads/2017/01/atlas-library-structure.png’ width=’300’/>
这次,main.swift 会被 Atlas.swift 所替代。这个文件以及在 Sources/ 目录下的文件都会被导入到这个库中。实际上,库和可执行包的区别就在于有没有 main.swift。
这次我们需要用一个测试。用 swift test 来运行这个测试。Swift 包管理器会编译库并运行测试。
注意:你会在 Tests/ 目录下看到 LinuxMain.swift 文件,在 AtlasTests 的测试案例类中还有一个 allTests 属性。对于要在 Linux 上运行 XCTest 测试而言,这两者都是必须的。Linux 没有 O-C 运行时,而 O-C 运行时会自动查找以 test 开头的方法并运行它。每当添加一个测试方法,你都需要修改这个 allTests 属性,每当添加一个新的测试案例类,你都需要修改 LinuxMain.swift。
打开 Atlas.swift 修改内容为:
public struct Country {
public let code: String
public init(code: String) {
self.code = code.uppercased()
}
public var emojiFlag: String {
return "\u{1f1f5}\u{1f1f7}"
}
}
这里我们实现了一个 Country 结构,它通过一个 ISO 国别代码进行初始化。emojiFlag 属性根据国别代码返回对应的国旗字符。现在,你需要编写测试。
注意每个方法和属性都标记为公有的,这样它的每个成员对于使用这个库的代码来说都是课件的:]
打开 AtlasTests.swift,编辑内容为:
import XCTest
@testable import Atlas
class AtlasTests: XCTestCase {
func testAustria() {
XCTAssertEqual(Country(code: "AT").emojiFlag, "\u{1f1e6}\u{1f1f9}")
}
func testTurkey() {
XCTAssertEqual(Country(code: "TR").emojiFlag, "\u{1f1f9}\u{1f1f7}")
}
func testUnitedStates() {
XCTAssertEqual(Country(code: "US").emojiFlag, "\u{1f1fa}\u{1f1f8}")
}
}
extension AtlasTests {
static var allTests : [(String, (AtlasTests) -> () throws -> Void)] {
return [
("testAustria", testAustria),
("testTurkey", testTurkey),
("testUnitedStates", testUnitedStates)
]
}
}
这里实现了 3 个测试。我们创建了 3 个不同的国籍,然后断言它们是否拥有正确的 emoji 国旗字符。
运行测试:
swift test
你会看到 3 个测试被运行,但全部失败。这说明我们还有很多事情要干啊 :]
现在我们测试失败了,我们要让它们通过测试。
emoji 国旗的工作原理非常简单:传入一个国家代码,比如 AT,然后将每个字符转换成设为的地区指示标志。例如, ?? 和 ??。然后将二者组合在一起,你就会得到 emoji 国旗!
https://koenig-media.raywenderlich.com/uploads/2017/01/ragecomic_flags.png’ width=’600’/>
回到 Atlas.swift 为 Country 结构增加一个方法:
func regionalIndicatorSymbol(unicodeScalar: UnicodeScalar) -> UnicodeScalar? {
let uppercaseA = UnicodeScalar("A")!
let regionalIndicatorSymbolA = UnicodeScalar("\u{1f1e6}")!
let distance = unicodeScalar.value - uppercaseA.value
return UnicodeScalar(regionalIndicatorSymbolA.value + distance)
}
这里,我们利用了字母和区域指示标志在 Unicode 表中的值都是连续排列的原理。比如 A 是 65,B 是 66,而 ?? 是 127462,?? 就是 127463。如果将字符 P 转换成区域指示标志,需要计算出 A 到 P 的值相差多少,然后将 ??加上这个差值就得到 ??。
这是最难的部分。写好这个方法后,剩下的事情就简单了。将 emojiFlag 属性定义修改为:
public var emojiFlag: String {
return code.unicodeScalars.map {
String(regionalIndicatorSymbol(unicodeScalar: $0)!) } .joined()
}
我们将国家代码的每个字母放到数组中,然后将每个字母转换成对应的区域指示标志,然后将它们连在一起。这就得到了国旗!
运行测试,3 个测试都通过了。
接下来将代码提交到 Git,并标记一个本号。因为这是我们的第一个版本,我们可以将版本标记为 1.0.0。
执行下列命令,创建 Git 存储库并标记版本:
git init
git add .
git commit -m "Initial commit"
git tag 1.0.0
创建可执行包
现在你已经准备好 Flag 库,我们可以将它添加为 Flag 可执行包的依赖。
回到 Flag 目录,打开 Package.swift 文件。它目前是这个样子:
import PackageDescription
let package = Package(
name: "Flag"
)
每个 Swift 包都有一个类似的 PackageDescription。最重要的参数是 dependencies 参数。
将包描述修改为这样:
let package = Package(
name: "Flag",
dependencies: [
.Package(url: "../Atlas", "1.0.0")
]
)
这里,我们声明 Flag 包拥有一个依赖,这个依赖的 URL 是 ../Atlas,版本是 1.0.0。
版本号应该使用语义上的版本号。简单说,就是类似于 MAJOR.MINOR.PATCH 这样的版本号。MAJOR 版本表示的是不向后兼容的修改,MINOR 版本表示向后兼容的修改,PATCH 版本是 bug 的修复。关于语义版本,细节可参考 这里。
大部分情况下,你只想让新版本在 bug 修复和小版本改进上做修改。幸好,Swift 包管理器允许我们这样做。将包描述修改为:
let package = Package(
name: "Flag",
dependencies: [
.Package(url: "../Atlas", majorVersion: 1)
]
)
包管理器提供了你在更新库时想精确控制的版本。请参考这里。
编译包:
swift build
Swift 包管理器将抓取、编译库并将之连接到你的可执行包。现在的文件夹结构将是这个样子:
https://koenig-media.raywenderlich.com/uploads/2017/01/flag-structure.png’ width=’300’/>
你会看到 Swift 包管理器已经细心地根据你的要求将 1.0.0 版本安装到项目中了。打开 main.swift 文件,编辑内容如下:
import Atlas
let arguments = CommandLine.arguments
if arguments.count != 2 {
print("USAGE: flag [iso country code]")
} else {
let code = arguments[1]
let country = Atlas.Country(code: code)
print(country.emojiFlag)
}
这里,我们导入了 Atlas 库,根据命令行参数的第一个参数,打印出对应的国旗 emoji。如果缺少参数,我们会打印命令帮助。
编译运行 app:
swift build
./.build/debug/Flag US
你在终端窗口中看到了美国国旗!
在你和你的 app 玩得不亦乐乎的时候,我们该来打包它 了。最后编译一下 app,这次需要加上 release 优化参数:
swift build --configuration release
现在你可以这样运行你的 release 版 app 了:
./.build/release/Flag PR
你可以将 ./.build/release/Flag 文件打包压缩,然后分享给你的朋友、家人或其它人了 :]
用包管理器生成 Xcode 项目
老旧的命令行和文本编辑器很酷,但你可能是一个 iOS 或 macOS 程序员,你使用的是 Xcode。别担心——大部分工作 Xcode 都可以做得很好。
回到 Atlas 包下面,生成一个 Xcode 项目:
cd ../Atlas
swift package generate-xcodeproj
这会生成一个 Atlas.xcodeproj 文件。你可以用 Xcode 打开这个项目,像其他 Xcode 项目一样编译包和运行测试。
https://koenig-media.raywenderlich.com/uploads/2017/01/xcode-650x357.png’ width= ‘600’/>
同样的方法可以用在 Flag 包上。在 Flag 文件夹下面执行 swift package generate-xcodeproj 命令,生成 Flag.xcodeproj。
cd ../Flag
swift package generate-xcodeproj
用 Xcode 打开项目,确认 Flag 可执行目标是否选中,它显示一个小的终端窗口的图标。现在你也可以编译和运行这个包了。
为了指定可执行命令的参数,请进入 Product\Scheme\Edit Scheme… 窗口,选择 Run\Arguments ,在 Arguments Passed On Launch 中添加一个参数比如 US:
https://koenig-media.raywenderlich.com/uploads/2017/01/Flag_xcodeproj-650x361.png’ width=’600’/>
注意,Xcode 无法添加和编译依赖,因此你无法完全离开命令行。
结束
你可以从这里下载完成的 Swift 包管理器项目。
你还可以参考Swift 官网关于包管理器的章节。
关于包描述选项的最新文档,请参考github。
关于 Swift 4 的包管理器修改方向,你可以参考 Swift 演进路线图的邮件列表。
你还可以参考IBM 的 Swift Package Catalog,它可以帮助你找到可以用在你项目中的新包。
在 Swift 包管理器能够支持非主机平台之前,你仍然不得不使用 Cocoapods 或 Carthage 来构建 iOS、watchOS 和 tvOS app。
留作今天的作业,你可以将你的库推到 GitHub,然后在 Atlas 中使用它的远程依赖。提示:只需要将 dependency 的 url 参数修改为 GitHub URL。
尝试添加新的功能,比如当没有提供任何参数时,列出所有国家的名字和国旗。提示:你将需要用到 Locale.isoRegionCodes。
在 Atlas 库中实现你的新功能,然后创建新的版本,比如 1.1.0,然后在 Flag 中使用这个新版本。确认你在包描述中使用了新版本,然后通过 Swift 包管理器将依赖升级到新版本。
将你的答案公布到下面的留言中,祝你开心!
Swift 包管理器教程