包管理器

Swift 包管理器 (SwiftPM) 是一款用于管理 Swift 代码分发的工具。它与 Swift 构建系统集成,可以自动化下载、编译和链接依赖项的过程。

包管理器包含在 Swift 3.0 及更高版本中。

概念概览

本节介绍 Swift 包管理器功能背后的基本概念。

模块

Swift 将代码组织成模块。每个模块指定一个命名空间,并对模块外部可以使用哪些代码部分强制执行访问控制。

一个程序可以将其所有代码放在一个模块中,或者它可以导入其他模块作为依赖项。除了少数系统提供的模块(例如 macOS 上的 Darwin 或 Linux 上的 Glibc)外,大多数依赖项都需要下载和构建代码才能使用。

当您为解决特定问题的代码使用单独的模块时,该代码可以在其他情况下重用。例如,一个提供网络请求功能的模块可以在照片共享应用程序和天气应用程序之间共享。使用模块可以让您在其他开发人员的代码之上构建,而不是自己重新实现相同的功能。

一个由 Swift 源文件和一个清单文件组成。清单文件名为 Package.swift,它使用 PackageDescription 模块定义包的名称及其内容。

一个包有一个或多个目标。每个目标指定一个产品,并且可以声明一个或多个依赖项。

产品

目标可以构建库或可执行文件作为其产品。包含一个可以被其他 Swift 代码导入的模块。可执行文件是可以由操作系统运行的程序。

依赖项

目标的依赖项是包中代码所需的模块。依赖项由包源的相对或绝对 URL 以及一组可用于包版本的需求组成。包管理器的作用是通过自动化下载和构建项目所有依赖项的过程来降低协调成本。这是一个递归过程:依赖项可以有自己的依赖项,每个依赖项也可以有依赖项,从而形成依赖关系图。包管理器下载并构建满足整个依赖关系图所需的一切内容。


以下部分假设您已掌握 Swift 的工作知识。如果您是该语言的新手,您可能需要首先查阅入门资源之一。我们推荐The Swift Programming Language 中的 Swift 游览

如果您想跟随代码示例进行操作,则需要安装可用的 Swift。您可以在入门中找到有关如何安装 Swift 的说明。

示例用法

入门中,使用 Swift 包管理器构建了一个简单的命令行工具。

为了更完整地了解 Swift 包管理器的功能,以下示例包含三个相互依赖的包

您可以通过从 GitHub 上的 Dealer 项目下载源代码并运行以下命令来构建和运行完整示例

$ git clone https://github.com/apple/example-package-dealer.git
$ cd example-package-dealer
$ swift run dealer <count>

创建库包

我们将从创建一个目标开始,该目标表示标准 52 张牌组中的一张扑克牌。PlayingCard 目标定义了 PlayingCard 类型,该类型由 Suit 枚举值(梅花、方块、红桃、黑桃)和 Rank 枚举值(A、2、3、...、J、Q、K)组成。

public enum Rank: Int {
    case two = 2
    case three, four, five, six, seven, eight, nine, ten
    case jack, queen, king, ace
}

public enum Suit: String {
    case spades, hearts, diamonds, clubs
}

public struct PlayingCard {
    let rank: Rank
    let suit: Suit
}

按照惯例,目标包括位于 Sources/<target-name> 目录中的任何源文件。

example-package-playingcard
├── Sources
│   └── PlayingCard
│       ├── PlayingCard.swift
│       ├── Rank.swift
│       └── Suit.swift
└── Package.swift

由于 PlayingCard 目标不生成可执行文件,因此可以将其描述为。库是一个目标,它构建一个可以被其他包导入的模块。默认情况下,库模块公开位于 Sources/<target-name> 目录中的源代码中声明的所有 public 类型和方法。

当创建旨在用作其他项目依赖项的库包时,Package.swift 清单必须位于包目录结构的最顶层/根目录。

运行 swift build 以启动 Swift 构建过程。如果一切正常,它将为 PlayingCard 编译 Swift 模块。

PlayingCard 包的完整代码可以在 https://github.com/apple/example-package-playingcard 中找到。

导入依赖项

DeckOfPlayingCards 包引入了之前的包:它定义了 Deck 类型。

要使用 PlayingCards 模块,DeckOfPlayingCards 包必须在其 Package.swift 清单文件中将该包声明为依赖项。

// swift-tools-version:5.3
import PackageDescription

let package = Package(
    name: "DeckOfPlayingCards",
    products: [
        .library(name: "DeckOfPlayingCards", targets: ["DeckOfPlayingCards"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/example-package-playingcard.git", from: "3.0.0"),
    ],
    targets: [
        .target(
            name: "DeckOfPlayingCards",
            dependencies: ["PlayingCard"]),
        .testTarget(
            name: "DeckOfPlayingCardsTests",
            dependencies: ["DeckOfPlayingCards"]),
    ]
)

每个依赖项都指定一个源 URL 和版本要求。源 URL 是当前用户可以访问的 URL,该 URL 解析为 Git 存储库。版本要求遵循 语义版本控制 (SemVer) 约定,用于确定要检出和用于构建依赖项的 Git 标签。对于 PlayingCard 依赖项,将使用主版本等于 3 的最新版本。

当运行 swift build 命令时,包管理器会下载所有依赖项,编译它们,并将它们链接到包模块。这允许 DeckOfPlayingCards 通过 import 语句访问其依赖模块的公共成员。

您可以在项目根目录的 .build/checkouts 目录中查看下载的源文件,并在项目根目录的 .build 目录中查看中间构建产品。

DeckOfPlayingCards 包的完整代码可以在 https://github.com/apple/example-package-deckofplayingcards 中找到。

解决传递依赖项

在完成所有其他步骤后,您现在可以构建 Dealer 模块。Dealer 模块依赖于 DeckOfPlayingCards 包,而后者又依赖于 PlayingCard 包。但是,由于 Swift 包管理器会自动解析传递依赖项,因此您只需将 DeckOfPlayingCards 包声明为依赖项即可。

// swift-tools-version:5.5

import PackageDescription

let package = Package(
    name: "dealer",
    products: [
        .executable(name: "Dealer", targets: ["Dealer"]),
    ],
    dependencies: [
        .package(url: "https://github.com/apple/example-package-deckofplayingcards.git", from: "3.0.0"),
    ],
    targets: [
        .target(
            name: "Dealer",
            dependencies: ["DeckOfPlayingCards"]),
    ]
)

Swift 要求源文件导入代码中引用的任何类型的模块。对于 Dealer 模块的 main.swift 文件,引用了 DeckOfPlayingCards 中的 Deck 类型和 PlayingCard 中的 PlayingCard 类型。

import DeckOfPlayingCards

let numberOfCards = 10

var deck = Deck.standard52CardDeck()
deck.shuffle()

for _ in 1...numberOfCards {
    guard let card = deck.deal() else {
        print("No More Cards!")
        break
    }

    print(card)
}

按照惯例,目录中包含名为 main.swift 的文件的目标会生成可执行文件。

运行 swift build 命令会启动 Swift 构建系统以生成 Dealer 可执行文件,该文件可以从 .build/debug 目录运行。

$ swift build
$ ./.build/debug/Dealer
♠︎6
♢K
♢2
♡8
♠︎7
♣︎10
♣︎5
♢A
♡Q
♡7

Dealer 包的完整代码可以在 https://github.com/apple/example-package-dealer 中找到。


有关使用 Swift 包管理器的更多信息,请参阅 GitHub 上的 Swift Package Manager 项目中提供的文档。