Swift Package ManagerTutorial 翻译

Swift Package Manager 教程

翻译自原文

是时候学习如何使用 Swift Package Manager 去处理外部依赖、在 macOS 和 linux 上创建你自己的 Swift 库和 app 了。


Swift Package Manager 基础

注意:由于 Apple 在 Swift 4 中为 Swift Package Manager 的 API 做了一个完全的重新设计(你可以阅读相关更改的内容),本教程并不适用于 Swift 3。 所以首先在我们开始之前,请在检查你的设备上的 Swift 版本。

swift --version
Apple Swift version 4.0.2 (swiftlang-900.0.69.2 clang-900.0.38)
Target: x86_64-apple-macosx10.9

创建 apps

所有困难的工作都可以交由 swift package 命令来完成。你可以在终端中输入该命令查看可用的子命令。为了生成一个新的包,你应该使用初始化命令。如果你不提供一个类型标志,默认情况下该命令将会生成一个库,但这一次我们想要生成一个可执行的应用。

swift package init --type executable 
swift build 
swift run my-app

编译器可以在 swift build 命令的帮助下构建你的源文件。该可执行文件将会被放置在 .build/ 目录下的某个地方,如果你使用 swift run my-app 命令来运行刚刚创建的应用,你应该呢个看到基本的 'Hello, world!' 消息。

祝贺你成功生成了你的第一个命令行 Swift 应用程序!

现在你应当来做一些切实的编码了。通常而言你的 Swift 源文件应该在 Sources 目录下, 然而你可能想要为你的 app 创建一些可复用的部分。所以让我们通过创建一个全新的库来为这个场景做准备吧。

生成一个 Library

我们依旧从从初始化命令开始,但这次我们不指定 init 的类型了。实际上我们可以键入 swift package init --type library 但这需要键入很多单词。 😜 另外由于我们正在生成一个库, SPM 将会给我们提供一些基本的测试,让我们使用 swift test 命令来运行它们吧。

swift package init
swift test

如果你此时检查文件结构,你会在 Tests 下看到一个单元测试的例子,而不会在 source 中找到 main.swift 文件。

现在你有了一些基础了。你有一个示例程序和一个 library, 让我们在 Swift Package Manager Manifest API 的帮助下将它们链接在一起吧。

The Manifest API(清单 API) - Package.swift

每一个 SPM 包内都有一个 Package.swift 清单文件(manifest file). 在清单文件中你可以定义你的全部依赖、targets 甚至为你的工程定义指定的源文件.在本节我会教你清单文件 (manifest file) 的一些基础内容.

工具版本

首先如果你想支持新的 manifest file 的格式 (换言之, Swift 4 版本) , 你得以注释的方式在你的 manifest 文件设置 swift-tools-version.

// swift-tools-version:4.0

现在你已经准备好在全新的 manifest API 下工作了!

依赖

让我们先通过在 Package.swift 文件中创建一个新的包依赖 (package dependency) 来为主程序添加一个依赖库. 第一个参数是一条包 url 字符串,可以是本地文件路径也可以是远程 url (通常是一个 github 仓库链接). 注意, 你还应当将你的依赖添加到 targets 中. 通常而言包的特定名称 (包名) 已经在该 library 的 manifest 文件中定义了.

// swift-tools-version:4.0

import PackageDescription

let package = Package(
    name: "my-app",
    dependencies: [
        .package(url: "../my-lib", .branch("master")),
    ],
    targets: [
        .target(
            name: "my-app",
            dependencies: ["my-lib"]
        ),
    ]
)

如果你现在运行 swift build 你将编译源文件失败. 那是因为 SPM 仅会工作在 git 仓库下. 这就意味着你需要为你的 library 创建一个 git 仓库. 让我们移动到该目录并执行如下命令:

git init
git add .
git commit -m 'initial'

你也应当标记我们指定的包依赖的分支。你可以使用 version 号,甚至也可以用 commit hashes 。所有可用的选项都很好的被写在了 manifest api redesign proposal 文档中。

现在让我们回到应用目录并使用 swift package update 命令来更新以来。这次它就能够获取、克隆并成功搞定我们的依赖了。

你可以构建并运行,然而我们忘了设置我们 library 中结构体的访问级为 public, 所以从该 API 中看不到任何东西。

public struct my_lib {
    public var text = "Hello, World!"

    public init() {}
}

让我们来做一些修改并将其提交到该 library 的主分支上。

git add .
git commit -m 'access level fix'

你已经准备好在 app 中使用该 lib 了,修改 main.swift file 如下。

import my_lib

print(my_lib().text)

再一次更新依赖,这次我们来构建一个 release 构建。

swift package update
swift build -c release
swift run -c release

通过 -c--configuration 标识你可以生成一个 release 构建。

Products 与 targets

默认情况下, SPM 与下列 targets 目录一起工作:

Regular targets: package root, Sources, Source, src, srcs.
Test targets: Tests, package root, Sources, Source, src, srcs.

这就表明,如果你在这些目录下创建 .swift 文件,这些源文件将根据文件路径被编译或测试。另外,生成的 mainfest 文件将仅仅包含一份构建 target (像 Xcode targets一样) , 但有时候你想要从同一个 bundle 中创建多个 apps 或 libraries. 让我们稍微修改下我们的 Package.swift, 并看看如何能创造一个全新的 target.

// swift-tools-version:4.0

import PackageDescription

let package = Package(
    name: "my-app",
    dependencies: [
        .package(url: "../my-lib", .branch("master")),
        .package(url: "https://github.com/kylef/Commander", from: "0.8.0"),
    ],
    targets: [
        .target(
            name: "my-app",
            dependencies: ["my-lib"]
        ),
        .target(
            name: "my-cmd",
            dependencies: ["Commander"],
            path: "./Sources/my-cmd",
            sources: ["main.swift"]
        ),
    ]
)

我们刚刚从 github 创建了一个新的依赖 和一个在 Sources/my-cmd 目录下只含有 main.swift 文件的全新的 target. 现在让我们创建这个目录并为这个新的 app 添加一些源码.

import Foundation
import Commander

let main = command { (name:String) in
    print("Hello, \(name.capitalized)!")
}

main.run()

使用 swift build 编译这个工程并用一个额外的 name 参数运行这个新创建的 app. 希望你能看到的输出如下:

swift run my-cmd guest
// Hello, Guest!

因此我们刚刚创建了一个全新的可执行的 target, 但如果你想将你的 targets 暴露给其他的 packages , 你应当也将它们定义为 products. 如果你打开这个 library 的 manifest 文件, 你会看到里面有个从该 library target 定义 target 字段. 通过这种方式, 包管理器可以根据给定的 product name 链接 product 依赖.

注意: 你可以定义 static 或 dynamic 的 libraries. 但推荐使用 automatic , 这样可以让 SPM 决定合适的链接.

// swift-tools-version:4.0
import PackageDescription


let package = Package(
    name: "my-lib-package",
    products: [
        .library(name: "my-lib", targets: ["my-lib"]),
        //.library(name: "my-lib", type: .static, targets: ["my-lib"]),
        //.library(name: "my-lib", type: .dynamic, targets: ["my-lib"]),
    ],
    dependencies: [
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        .target(
            name: "my-lib",
            dependencies: []),
        .testTarget(
            name: "my-libTests",
            dependencies: ["my-lib"]),
    ]
)

部署目标(Deployment target), 其他 build flags

有时候你需要为你的包指定部署的 target. 现在 Swift Package Manager 已经能做到了 (已经有 一段时间了). 你只需要在构建阶段为编译器提供一些额外的参数就行了.

swift build -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.12"

定义构建的 flags 也是可行的.

swift build -Xswiftc "-D" -Xswiftc "DEBUG"

现在在你的源码里面你可以检查 DEBUG 标志的存在了.

#if DEBUG
    print("debug mode")
#endif

如果你想了解更多关于构建过程的内容, 只需要键入 swift build --help 你就可以看到该构建命令可用的选项.

还有一件事

你可以通过 Swift Package Manager 来生成 Xcode Projects。

swift package generate-xcodeproj

这就是 SPM 的简单介绍了。实际上我们不仅仅介绍了 Swift Package Manager 的基础知识,也稍微拓展了一些,现在你应该对 targets、products 以及大部分可用的命令比较熟悉了,但仍然还有很多需要学习。所以如果你想对这个 amazing 的工具了解更多,这里还有一些非常棒的资源等着你去浏览。 Enjoy! 😉

另外,本教程的源码可以在 github 上获取。

拓展阅读

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,616评论 4 59
  • 春天来了,然而我并没有带春天的衣服,这个月的零花钱也花的差不多了,只够生活。我就想让妈妈支援我一点,买个外套。 晚...
    丹阳world阅读 304评论 0 0
  • 7组65号梁小美 一直是叶老师和时间管理的崇拜者,偶然得到了叶老师的《善用时间》,又虔诚求得了叶老师的亲笔签名,更...
    G192小美阅读 165评论 0 0
  • 昨日,期待已久的李宇春《野蛮生长》的第一张EP《野》的第一首主打歌《存在感》正式来袭。“云层肥厚,天地很宽,疯狂爱...
    三毛的撒哈拉阅读 344评论 2 1
  • 努力曾把生活过成诗 却忘记生活是由 90%的苟且 9....
    今析何兮阅读 260评论 2 3