如何建立並發佈 Swift Packages

Photo by Heather Barnes on Unsplash
Photo by Heather Barnes on Unsplash
Swift packages 是可重複使用的程式碼元件。它可包含程式碼、二進位檔、和資源檔。我們可以很容易地在我們的 app 專案中使用 Swift packages。本文章將介紹如何建立並發佈 Swift packages。

Swift packages 是可重複使用的程式碼元件。它可包含程式碼、二進位檔、和資源檔。我們可以很容易地在我們的 app 專案中使用 Swift packages。本文章將介紹如何建立並發佈 Swift packages。

完整程式碼可以在 下載。

建立 Swift Packages

有兩種方式可以建立 Swift packages。一種是使用 Xcode,另一種是使用 command line。

用 Xcode 建立 Swift Packages

Xcode 支援建立、發佈、和管理 Swift packages。使用 Xcode 建立 Swift packages 是相當簡單的。打開你的 Xcode,選擇 File -> New -> Package。

Xcode -> New -> Package
Xcode -> New -> Package

Xcode 會跳出下面的視窗,要求你輸入 Swift package 的名稱,以及選擇儲存的路徑。我們輸入 Greeting 作為我們的 Swift package 的名稱。

Name your Swift package.
Name your Swift package.

按下 Create 之後,Xcode 會產生一個 Swift package,如下圖。一個標準的 Swift package 包含一個 Package.swift、Sources 資料夾、和 Tests 資料夾。

  • Package.swift:Swift package 的配置檔。我們之後會詳細地介紹它。
  • Sources/:資料夾下有一個和此 Swift package 名稱一樣的資料夾叫 Greeting。所有的程式碼都應該在這下面。
  • Tests/:資料夾下有一個資料夾,其名稱是此 Swift package 名稱加上 Tests。所有的測試程式碼都應該在這下面。
The project structure of a Swift package.
The project structure of a Swift package.

用 Command Line 建立 Swift Packages

使用 command line 來建立 Swift packages 也是很容易的。首先先建立一個資料夾,其名稱將會是 Swift package 的名稱。然後,在此資料夾下,輸入 swift package init --type library,它會產生 Swift package 專案。

% mkdir Greeting
% cd Greeting
Greeting % swift package init --type library
Creating library package: Greeting
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/Greeting/Greeting.swift
Creating Tests/
Creating Tests/GreetingTests/
Creating Tests/GreetingTests/GreetingTests.swift

Package.swift

Package.swift 是 Swift package 的配置檔。它裡面主要的使用一個 Package 物件來定義所有的配置。

Package.swift 的第一行永遠是定義此 Swift package 要使用的 Swift tool 版本號。使用 5.4 和之後的版本的話,可以在前面加上空白,也可以不加。但是使用 5.3 和之前的版本的話,則前面不可以加上空白。

Swift tool 版本定義了:

  • PackageDescription library 的版本。
  • Swift tools 和 Swift 相容語言的最低版本。它們之後會用來執行此配置檔。
  • 要使用此 Swift package 時,所需最低版本的 Swift tools。
// swift-tools-version: 5.4

Package 的建構子如下。其有不少參數,我們將只介紹幾個常用的參數。

Package.init(
    name: String,
    defaultLocalization: LanguageTag? = nil,
    platforms: [SupportedPlatform]? = nil,
    pkgConfig: String? = nil,
    providers: [SystemPackageProvider]? = nil,
    products: [Product] = [],
    dependencies: [Package.Dependency] = [],
    targets: [Target] = [],
    swiftLanguageVersions: [SwiftVersion]? = nil,
    cLanguageStandard: CLanguageStandard? = nil,
    cxxLanguageStandard: CXXLanguageStandard? = nil
)

name 定義此 Swift package 的名稱。

platforms 列出此 Swift package 支援的 platforms。SupportedPlatform 定義了所有可選用的 platforms。

products 列出此 Swift package 最後會產生的 products。當其他的 Swift package 使用此 Swift package 時,會看到的 product 列表。Product 定義了三種 products:

  • .library(name:type:targets):產生出一個 library。整合此 library 的 Swift packages 可以使用其 public APIs。
    • name:此 library product 的名稱。
    • type:決定整合此 library 的 Swift packages 該如何 link 此 library。若不指定此參數,則 Swift Package Manager 可根據整合此 library 的 Swift packages 的偏好設定來決定。
      • static:定義此為 statically linked library。
      • dynamic:定義此為 dynamically linked library。
    • targets:定義此 library product 是由哪些 targets 產生的。這些 targets 是定義在 Package.init() 裡的 targets 參數。
  • .executable(name:targets:):產生出一個 executable。
  • .plugin(name:targets:):產生出一個 Swift package plugin。

dependencies 列出此 Swift package 所倚賴列表。Package.Dependency 定義一個依賴的來源與版本。

  • package(path:):從給定的 path,加入一個 local package 作為依賴。
  • package(url:from:):從給定的 url,加入一個 remote package 作為依賴。其版本可從指定的最低版本至下一個 major 版本。

targets 列出此 Swift package 會產生出的 targets。與 products 不同的是,一個 product 是由一個或數個 targets 一起 bundle 而成的。Target 定義一個 target。

  • .target(name:dependencies:):建立一個 library target。
    • name:此 target 的名稱。
    • dependencies:指定此 target 依賴其他的 targets。如果此 target 依賴一個定義在 Package.init() 的 dependencies 的話,也必須要在這邊指定。
  • .testTarget():建立一個 test target。
  • .binaryTarget():建立一個 binary target。這個 target 會參考到 path 所指的 binary artifact。
  • .systemLibrary():建立一個 system library target。
  • .executableTarget():建立一個 executable target。
  • .plugin():建立一個 package plugin target。

以下是 Greeting package 的 Package.swift。我們在 dependencies 中加入一個依賴叫 SwiftyJSON。然後,在 targets 中,我們也要指名 target Greeting 依賴於 SwiftyJSON。

// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Greeting",
    platforms: [
        .iOS(.v13)
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Greeting",
            targets: ["Greeting"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
        .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "Greeting",
            dependencies: ["SwiftyJSON"]),
        .testTarget(
            name: "GreetingTests",
            dependencies: ["Greeting"]),
    ]
)

發佈 Swift Packages

發佈 Swift packages 給其他的 Swift packages 使用是很簡單的。我們只要將我們的 Swift package 放到一個 Git repository,那其他的 Swift packages 就可以透過這個 Git repository 的 URL 來我們的 Swift package 整合進專案裡。至於版本的話,我們可以透過 Git tag 來標注版本。

以上的發佈流程可以使用 Xcode、command line、或是任何 Git client app 來做。

用 Xcode 發佈 Swift Packages

點選 Source Control -> New Git Repository。

Source Control -> New Git Repository...
Source Control -> New Git Repository…

選擇為當前的 Swift package 創建 Git repository。

Create Git repositories for projects.
Create Git repositories for projects.

這時候目前 Swift package 的 Git repository 已經創建好了。Xcode 也為我們建立了第一筆 commit。

在右邊的 Navigator 上,點選 Show the source control navigator,並選擇 Repositories。然後在 Remotes 上點擊右鍵,並選擇 New “Greeting” Remote..。

New Git remote.
New Git remote.

這時 Xcode 會彈出下面的視窗。我們在 Account 欄位選擇 GitHub。它會要求我們輸入 GitHub 的 Account 和 Token 來登入 GitHub。

Sign in to your GitHub account.
Sign in to your GitHub account.

輸入好 GitHub 的 Account 和 Token 後,我們就可以在 GitHub 上建立一個 remote repository。值得注意的是,repository name 最好和我們的 Swift package 的名稱一樣,也就是 Greeting。

Create Git remote.
Create Git remote.

Xcode 幫我們建立好一個 remote repository 時,會順便 push 所有的 local commits 到 remote repository。

接下來,我們要建立一個 Git tag 來發佈版本 1.0.0。在我們最後一筆的 commit 上點擊右鍵,選擇 Tag “xxxxxxx”,其中 xxxxxxx 會被取代為該 commit 的 hash。

Create a Git tag.
Create a Git tag.

Xcode 會彈出以下視窗。我們輸入 tag 的名稱,也就是 1.0.0。

Create a new tag from revision.
Create a new tag from revision.

當建立好 Git tag 時,這個 Git tag 還只有在 local 而已。我們需要將它 push 到 remote repository。選擇 Source Control -> Push。

Source Control -> Push.
Source Control -> Push.

Xcode 會彈出以下視窗。務必點選 Include tags,然後點擊 Push 按鈕。這樣就完成發佈版本 1.0.0。

Push local changes including tags.
Push local changes including tags.

用 Command Line 發佈 Swift Packages

如果你的 Swift package 還不是 Git repository 的話,必須先用以下的指令在你的 Swift package 下建立一個 Git repository。

% cd Greeting
Greeting % git init
Initialized empty Git repository in /Users/wayne/Greeting/.git/
Greeting % git add .
Greeting % git commit -m "first commit"
[main (root-commit) 916bcf6] first commit
 5 files changed, 57 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 Package.swift
 create mode 100644 README.md
 create mode 100644 Sources/Greeting/Greeting.swift
 create mode 100644 Tests/GreetingTests/GreetingTests.swift
Greeting % git branch -M main

然後,在任何 Git hosting 中建立一個 Git repository。這個 Git repository 的名稱、Swift package 資料夾的名稱、和 Swift package 的名稱,三者最好都相同。在以下指令中,我們把 Swift package 送到 https://github.com/xhhuango/Greeting.git。

Greeting % git remote add origin https://github.com/xhhuango/Greeting.git
Greeting % git push -u origin main
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 16 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (11/11), 1.41 KiB | 1.41 MiB/s, done.
Total 11 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:xhhuango/Greeting.git
 * [new branch]      main -> main
branch 'main' set up to track 'origin/main'.

最後,我們用 Git tag 新增一個 1.0.0 的 tag,並將 tag 送到 remote Git repository。這樣就完成發佈版本 1.0.0。

Greeting % git tag 1.0.0
Greeting % git push origin --tags
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:xhhuango/Greeting.git
 * [new tag]         1.0.0 -> 1.0.0

加入 Swift Package 依賴

當發佈好我們的 Swift package 後,你就可以在你的 Xcode projects 中加入剛剛發佈的 Swift package。下面文章將詳細地介紹如何在你的 Xcode projects 中加入 remote 或 local Swift packages。

結語

Swift packages 是可重複使用的程式碼元件。與以往的 Xcode projects 不同的是,它不需要 Xcode 的專案檔,而是使用 Package.swift 來配置 Swift packages。比起 Xcode 的專案檔,Package.swift 清楚且簡單許多。

參考

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Chris Murray on Unsplash
Read More

Swift Combine 教學

Swift Combine 是 Apple 用來實現 reactive programming 的函式庫。在 Combine 還沒出來之前,我們一般是使用 RxSwift。不過,使用 Combine 的話,我們就不需要再引入額外的函式庫。而且與 RxSwift 相比,Combine 的效能更好。
Read More
Photo by Ben White on Unsplash
Read More

Swift Concurrency 教學

Swift 5.5 推出了 Swift concurrency。它讓我們用 synchronous 的方式來完成 asynchronous code。大大地降低 asynchronous code 的複雜度。本文章將介紹 Swift concurrency 的基本知識。
Read More