利用 Swift Packages 簡單地與團隊共享可重用的程式碼


在前一篇教學文章中,我們在 SwiftUI 創建了動畫導航選單 (navigation menu)。那如果我們想要重用其他專案的程式碼呢?當然,我們也可以就這樣從一個專案複製程式碼到另一個專案上;但更好的方法就是利用 Swift Packages 重用程式碼。

Swift Packages 是可重用的組件,開發者可以把組件匯入到自己的專案中。Swift Package Manager 是一個內建的工具,用於創建和管理 Packages,如此一來,我們就可以以 Packages 簡單地分享可重用的程式碼。

在這篇教學文章中,我會帶大家看看如何創建 Swift Packages,並把動畫導航選單的程式碼,轉換為可重用的 SwiftUI 組件。

請注意,我用了 Xcode 13 編寫範例程式碼。不過即使你正在使用較舊版本的 Xcode,應該還是可以跟上文章的步驟。

創建 Swift Packages

要創建 Swift Packages 有兩個方法:利用 Command Line 或 Xcode。

利用 Command Line

要用 Command Line 創建 Swift Package,我們可以打開 Terminal 並輸入以下 Command:

mkdir AnimatedMenuBar
cd AnimatedMenuBar
swift package init

資料夾的名稱就是 Package 的名稱,在這裡,我們使用了 AnimatedMenuBar。按下 return 後,我們會看到以下的訊息。

Creating library package: AnimatedMenuBar
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/AnimatedMenuBar/AnimatedMenuBar.swift
Creating Tests/
Creating Tests/AnimatedMenuBarTests/
Creating Tests/AnimatedMenuBarTests/AnimatedMenuBarTests.swift

這會創建出 Swift Package 的基本架構,包括 Source 和 Test。我們可以編輯 README.md 文件,來提供 Package Description。Package.swift 是一個 manifest 檔案,它會利用 PackageDescription 模塊來定義 Package 的名稱及內容。

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

import PackageDescription

let package = Package(
    name: "AnimatedMenuBar",
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "AnimatedMenuBar",
            targets: ["AnimatedMenuBar"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.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: "AnimatedMenuBar",
            dependencies: []),
        .testTarget(
            name: "AnimatedMenuBarTests",
            dependencies: ["AnimatedMenuBar"]),
    ]
)

請注意,Package 的 manifest 必須以字串 // swift-tools-version: 開頭,然後寫上版本號碼,例如:// swift-tools-version:5.3

利用 Xcode

如果想使用 Xcode 來創建 Package,我們可以到 File > New > Project…,然後在 Multiplatform 下選擇 Swift Package

Xcode-create-swift-packages

更新 Source 檔案

Sources 檔案下的 AnimatedMenuBar.swift 文件中,只包含了 Xcode 的預設內容:

public struct AnimatedMenuBar {
    public private(set) var text = "Hello, World!"

    public init() {
    }
}

我們需要添加創建動畫導航選單的程式碼。在這個範例中,我們可以重用上一篇文章的程式碼。

import SwiftUI

@available(iOS 14, macOS 11.0, *)
public struct AnimatedMenuBar: View {
    @Binding var selectedIndex: Int
    @Namespace private var menuItemTransition

    var menuItems = [ "Travel", "Nature", "Architecture" ]

    public init(selectedIndex: Binding<Int>, menuItems: [String] = [ "Travel", "Nature", "Architecture" ]) {
        self._selectedIndex = selectedIndex
        self.menuItems = menuItems
    }

    public var body: some View {

        HStack {
            Spacer()

            ForEach(menuItems.indices) { index in

                if index == selectedIndex {
                    Text(menuItems[index])
                        .padding(.horizontal)
                        .padding(.vertical, 4)
                        .background(Capsule().foregroundColor(Color.purple))
                        .foregroundColor(.white)
                        .matchedGeometryEffect(id: "menuItem", in: menuItemTransition)
                } else {
                    Text(menuItems[index])
                        .padding(.horizontal)
                        .padding(.vertical, 4)
                        .background(Capsule().foregroundColor(Color( red: 244, green: 244, blue: 244)))
                        .onTapGesture {
                            selectedIndex = index
                        }
                }

                Spacer()
            }

        }
        .frame(minWidth: 0, maxWidth: .infinity)
        .padding()
        .animation(.easeInOut, value: selectedIndex)

    }
}

因為是一個 Swift Package,我們需要把 AnimatedMenuBar 結構設置為 public。另外,我們也需要創建一個 public access level 的客製化 init

除了上述兩點之外,其他程式碼都幾乎都是一樣的。你可以也發現到另一個不同之處,就是我們用了 @available 屬性 (attribute) 來標註有可用性 (availability) 資訊的結構,這行程式碼表示這個結構只適用於 iOS 14 和 macOS 11.0(或更新的版本)。

修改 Test 程式碼

在預設情況下,Xcode 會產生一個 test 資料夾,讓我們放 automated test。我們可以把 test 程式碼放在 AnimatedMenuBarTests.swift 檔案中。但是在這個範例專案中,我們不會編寫程式碼。你可以如此註解排除 (comment out) 這行程式碼:

// XCTAssertEqual(AnimatedMenuBar().text, "Hello, World!")

添加 Dependency(非必須)

雖然這個 Package 不會依賴其他 Swift Packages,但如果需要的話,你還是可以編輯 dependencies 部分來添加 Dependent Package:

dependencies: [    
    .package(url: "https://url/to/dependency", from: 1.0.0)
],

添加支援平台

Swift Packages 應該提供多平台支援,但如果某個 Package 只支援特定平台,我們可以使用 Package.swift 中的 platforms 屬性。讓我們看看以下例子:

platforms: [
    .iOS(.v14),
    .macOS(.v11)
],

以這個 Package 為例,它會支援 iOS 14 和 macOS 11.0(或更新版本)。

把 Package 發佈到 GitHub

做好所有更改之後,我們就應該能夠 build package 以在本機 (local) 使用。如果要與團隊或社群中的開發者共享,我們就可以在 GitHub 上發佈 Package。

到 Xcode 選單選擇 Source Control > New Git Repositories…,以創建一個新的程式庫。

Xcode-create-git-repository

然後,轉到 Source Control Navigator,右擊 Remotes 並選擇 New “AnimatedMenuBar ” Remote…。

Xcode-remote-git-repository

如果你已經在 Xcode 設定好了 GitHub 帳戶,就應該可以創建一個遠端程式庫。把程式庫命名為 AnimatedMenuBar,並輸入 Package Description。你可以選擇把 Package 設為 public 或 private,在這個範例中,我會設定為 public。

create-remote-git

點擊 Create 按鈕後,Xcode 會在 GitHub 上創建程式庫,並把本機檔案上傳到程式庫。

目前,我們還沒有為 Package 設定版本號碼。要設定版本號碼,我們可以到 Source Control Navigator,右擊 Initial Commit 的條目,並選擇 Tag

swift-manage-git-tag

接著,把 Tag 設定為 1.0.0,並點擊 Create 來確認。

Xcode-add-tag-version

我們剛剛做的改變只限於本機,要為遠端程式庫設定 Tag,我們就需要把改變 push 到 GitHub。到 Xcode 選單,選擇 Source Control > Push,請記得要勾選 Include tags 方格,然後點擊 Push 按鈕。

git-push-tag

完成了!我們已經把 Swift Package 發佈到 GitHub 了。你可以在這裡找到我們這個 Swift Package。

使用 Swift Package

要在其他 Xcode 專案中使用 Swift Package,選擇 File > Add Package…,並在搜尋欄輸入 Package URL。

using-swift-package

然後,Xcode 就應該會顯示 Package 的描述和版本。點擊 Add Package,就可以下載並添加 Package 到專案中。

import-swift-package

下載 Package 後,我們會在 Project Navigator 中的 Package Dependencies 下看到 Package。現在,我們就可以在專案中使用 AnimatedMenuBar 視圖了。

我們只需要 import AnimatedMenuBar Package,並如此使用 AnimatedMenuBar 視圖就可以了:

import SwiftUI
import AnimatedMenuBar

struct ContentView: View {
    @State var tabIndex = 0

    var body: some View {
        AnimatedMenuBar(selectedIndex: $tabIndex)
    }
}

總結

在這篇教學文章中,我們介紹了創建 Swift Package 的步驟,以重用一些常見的 SwiftUI 視圖。這個技巧不只可應用於重用 SwiftUI 視圖,還可以應用於其他常用組件,來與團隊和專案分享。

你認為 Swift Packages 如何呢?有沒有使用 Swift Package Manager 來創建可分享的組件?歡迎留言與我分享。

譯者簡介:Kelly Chan-AppCoda 編輯小姐。
原文How to Share SwiftUI views Using Swift Packages


軟件工程師,AppCoda 創辦人。著有《iOS 13 App 程式設計實力超進化實戰攻略》、《iOS 13 App 程式設計實力超進化實戰攻略》以及《精通 SwiftUI》。曾任職於HSBC, FedEx等公司,專責軟體開發、系統設計。2012年創立AppCoda技術部落格,定期發表iOS程式教學文章。現時專注發展AppCoda,致力於iOS程式教學,產品設計及開發。

blog comments powered by Disqus
Shares
Share This