先前有寫過「如何將自己寫的套件上傳到 CocoaPods」
剛好 Swift Package Manager 也是近年 iOS App 開始使用的第三方套件管理工具
所以就來研究一下,該如何製作一個 Swift Package
就一般的建立 git repo,嗯對
README.md、.gitignore 都不用勾,只需要勾 LICENSE
因為後面在建立 Swift Package 的時候,會自己建立
建立 Swift Package 有兩種方法
首先,先在 Terminal cd 到要建立 Package 的目錄,
像是我要在桌面建立,那我就是 cd 到桌面
mkdir <你幫 Package 取的名稱> # 這一步可透過手動建立資料夾來替代
cd <你幫 Package 取的名稱>
swift package init --type <這個 Package 建立的類型>
swift package init --type
的 type 一共有四種類型
▲ 使用 type empty 建立的資料夾結構
▲ 使用 type empty 建立的 Xcode 結構
▲ 使用 type library 建立的資料夾結構
▲ 使用 type library 建立的 Xcode 結構
▲ 使用 type executable 建立的資料夾結構
▲ 使用 type executable 建立的 Xcode 結構
▲ 使用 type system-module 建立的資料夾結構
▲ 使用 type system-module 建立的 Xcode 結構
從 Xcode 新增有兩種方法,兩種方法建立出來的 Package type 都是 library
幫這個 Package 取名稱,預設名稱為 MyLibrary
按下 create 就會新增一個 Swift Package 了
使用 type library 或是 type executable 建立的 Swift Package
在 Sources 資料夾內都有預先建立好一個 swift 檔來告訴我們原始碼要放的位置
(type empty 也可以透過手動補齊的方式,變成 type library)
type library 會在 Sources 資料夾內建立一個跟 Package 同名的資料夾,裡面會有一個跟 Package 同名的 swift 檔
以我的為例就是 MyFirstSwiftPackageLibrary.swift
裡面已經有先宣告一個與 Package 同名的 struct
public struct MyFirstSwiftPackageLibrary {
/// 這個變數的 Access Level
/// - getter -> public
/// - setter -> private
/// - getter 跟 setter 同時指定時,只需要注明 setter 為 (set)
public private(set) var text = "Hello, World!"
/// 初始化
public init() {
}
}
那我們來改寫一下,讓他包含我們要的 Function
public struct MyFirstSwiftPackageLibrary {
/// 這個變數的 Access Level
/// - getter -> public
/// - setter -> private
/// - getter 跟 setter 同時指定時,只需要注明 setter 為 (set)
public private(set) var text = "Hello, World!"
/// 初始化
public init() {
}
public static func sayHello() {
print("MyFirstSwiftPackageLibrary say Hello!")
}
public static func inputAndOutput(input: String?) {
print("MyFirstSwiftPackageLibrary input \(String(describing: input)), and I output \(String(describing: input))")
}
}
這邊我們新增了 func sayHello()、func inputAndOutput(input: String?) 來做示範
預設會長得像下面這樣
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "MyFirstSwiftPackageLibrary",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "MyFirstSwiftPackageLibrary",
targets: ["MyFirstSwiftPackageLibrary"]),
],
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: "MyFirstSwiftPackageLibrary",
dependencies: []),
.testTarget(
name: "MyFirstSwiftPackageLibraryTests",
dependencies: ["MyFirstSwiftPackageLibrary"]),
]
)
我們看到第一行 // swift-tools-version:5.5
這邊的 5.5 指的是編譯時最低支援的 Swift 版本
後續有個 swiftLanguageVersions 參數可以指定相容的 Swift 版本
兩者之間必須以 // swift-tools-version:5.5
定義的版本為主
Example 1:
// swift-tools-version:5.5
swiftLanguageVersions: [.v5, .version(5.5)]
↑ 這樣可以 Build Succeeded,且無報錯
Example 2:
// swift-tools-version:5.5
swiftLanguageVersions: [.version(5.5.1)]
↑ 這樣可以 Build Succeeded,但會報錯
因為上方定義的 swift-tools 版本為 5.5
但這邊指定相容的 swift 版本為 5.5.1
swift version 5.5 不相容 swift version 5.5.1
因完整參數較多,這邊主要以常用參數來介紹,完整參數請查看下面官方文件
完整 Package 參數-Apple Developer Documentation
如果有寫錯的話,執行鍵旁邊的 Scheme 會消失,不讓你編譯!
這算是一種物理 debug???
name -> 這個 Package 的名稱
platforms -> 這個 package 所支援的平台
products -> 這個 Package 要對外呈現的方式
dependencies -> 這個 Package 所依賴的其他套件
targets -> 這個 Package 的 target List
swiftLanguageVersions -> 這個 Package 所相容的 Swift 版本
cLanguageStandard -> 這個 Package 所使用的 C 語言標準
cxxLanguageStandard -> 這個 Package 所使用的 C++ 語言標準
這個 package 所支援的平台,像是 iOS、macOS、watchOS、tvOS、DriverKit、Linux
寫法如下
platforms: [
.iOS(.v14),
.macOS(.v12),
.tvOS(.v14),
.watchOS(.v7),
.driverKit(.v21)
]
這個 Package 要對外呈現的方式,有三種
寫法如下
products: [
.library(
name: "MyFirstSwiftPackageLibrary",
targets: ["MyFirstSwiftPackageLibrary"]),
.executable(
name: "MyFirstSwiftPackageLibrary",
targets: ["MyFirstSwiftPackageLibrary"]),
.plugin(
name: "MyFirstSwiftPackageLibrary",
targets: ["MyFirstSwiftPackageLibrary"]),
]
這個 Package 所依賴的其他套件
這邊以之前所建立的 MyFirstPodLib 為例
寫法如下
dependencies: [
/// 指定版本
.package(url: "https://github.com/leoho0722/MyFirstPodLib.git", from: "0.0.4"),
/// 指定版本區間
.package(url: "https://github.com/leoho0722/MyFirstPodLib.git", "0.0.1" ... "0.0.4"),
/// 指定分支
.package(url: "https://github.com/leoho0722/MyFirstPodLib.git", branch: "master"),
/// 指定 Commit
.package(url: "https://github.com/leoho0722/MyFirstPodLib.git", revision: "172bc5cc5b92339f48346d549fc9212d50b1e68e"),
/// 本地路徑
.package(path: "../MyFirstPodLib"),
]
這個 Package 的 target List
因完整參數較多,這邊主要以常用參數來介紹,完整參數請查看下面官方文件
完整 Target 參數-Apple Developer Documentation
name -> Target 名稱
path -> Target 路徑,相依於 Package Root
預設為 [PackageRoot]/Sources/[TargetName]
exclude -> 不想包含在 Target 內的檔案路徑
sources -> 在 Target 內的原始碼檔案路徑
預設 TargetName 底下都是原始碼檔案,採用遞歸搜尋
resources -> 在 Target 中的 Resources Files
像是 `Image Files`、`Xib Files`、`Text Files`、`Folder` 等
publicHeadersPath -> C 語言的公開標頭檔路徑
cSettings -> C 語言檔案編譯時的 Build Settings
cxxSettings -> C 語言檔案編譯時的 Build Settings
swiftSettings -> Swift 檔案編譯時的 Build Settings
linkerSettings -> Package 內有用到的系統內建 Framework,要寫在這裡
在 Target 中的 Resources Files
Swift tools version 5.3 起支援將 Resources Files 一同打包到 Swift Package 內
下圖是不需要在 Package.swift 內標示的 Resources file types
因為 Xcode 能夠自動辨識其類型
▲ 圖截自 WWDC 2020-Swift packages: Resources and localization
下圖是需要在 Package.swift 內標示的 Resources file types
因為 Xcode 不能夠自動辨識其用途
▲ 圖截自 WWDC 2020-Swift packages: Resources and localization
其中分為 func process() 與 func copy()
/// 會根據所使用的平台,對 Resouces Files 進行相對應的優化
/// 大部分的 Resources Files 都應該使用 process
func process() # 推薦
/// Xcode 無法辨識用途的檔案、不根據平台進行優化,只進行拷貝
/// 如下圖的 Game Data 資料夾
func copy()
▲ 圖截自 WWDC 2020-Swift packages: Resources and localization
Package 內有用到的系統內建 Framework,要寫在這裡
寫法如下
linkerSettings: [
.linkedFramework("UIKit", .when(platforms: [.iOS])),
.linkedFramework("AppKit", .when(platforms: [.macOS]))
]
這個 Package 所相容的 Swift 版本
這邊所定義的 Swift 版本不能大於最上方宣告的 // swift-tools-version:5.5
範例請看上方的 // swift-tools-version:5.5
寫法如下
/// 單一版本
swiftLanguageVersions: [.v5]
/// 多個版本
swiftLanguageVersions: [.v4_2, .v5]
/// 指定版本
swiftLanguageVersions: [.version("5.5")]
就一般的 git push、git tag,嗯對
這部分跟 CocoaPods 蠻不一樣的
在一個專案內 Add Package,貼上 Git Repo 網址
在要用的檔案 import MyFirstSwiftPackageLibrary
執行結果
這邊也整理一下新增套件的流程
步驟0:先建立一個要用來存放套件的 git repo
步驟1:在本地建立一個用來存放套件的資料夾
步驟2:用 Terminal 切到剛剛建立用來存放套件的資料夾目錄底下
步驟3:在 Terminal 輸入 swift package init --type <這個 Package 建立的類型>
or Xcode File -> New -> Add Package
步驟4:打開 Package.swift (步驟3 用 Terminal 才需要)
步驟5:將 Source Code、圖片、Color Set 等資源放入 Sources/<Package 名稱> 資料夾內
步驟6:撰寫 Package.swift (看 Scheme 有沒有出現就知道有沒有寫錯了)
步驟7:Command+B 編譯,看是否成功編譯以及無錯誤
(target 裡面的 sources 路徑定義錯誤,會有警告訊息,要注意!!!!)
步驟8:將 Source Code 上傳到 git repo
步驟9:為剛剛上傳到 git repo 的套件新增 git tag
步驟10:測試是否可以在其他專案成功安裝
步驟11:(optional):修改 README.md
有推出,就會有更新
所以這裡就要來講説,當要更新套件的時候,要怎麼來更新
更新流程如下
步驟1:更新 Source Code、圖片、Color Set 等檔案
步驟2:將更新過後的套件 git 到 git repo
步驟3:git tag <新版號>
步驟4:git push --tags,將新版號 push 到 git repo
步驟5:測試是否可以在專案內成功抓到新版本的套件
步驟6 (optional):修改 README.md