標籤:工作原理 方法 依賴 des 開始 運行 結構 生態 country
原文:An Introduction to the Swift Package Manager
Mikael Konutgan
譯者:kmyhy
Swift 包管理器的正式發布是隨著 Swift3.0 一起發布的,它是一個用於構建能夠運行在 macOS 和 Linux 上的 Swift 庫和 app 的新方法。它能夠協助你管理依賴,讓你輕鬆構建、測試和運行你的 Swift 代碼。
Swift 包管理器有助於極大地改進 Swift 生態系統,讓 Swift 更容易使用、部署到沒有 Xcode 的平台上,比如 Linux。Swift 包管理器還能解決在使用多個相互依賴的庫時的“依賴地獄”的問題。
重要的一點是,因為 Swift 3 的原因,Swift 包管理器只能在 host 平台上編譯。換句話說,你無法編譯、使用 iOS、watchOS 和 tvOS 上的包。
讓我們開始吧!
開始
在開始之前,確認你已經安裝了 Swift 3.0 以上。Swift 3 內建在 Xcode8+,因此如果你已經安裝了 Xcode8 或以上,你就可以開始本教程。當然實際上你並不需要用 Xcode 去完成本教程,你可以直接從 swift.org 下載安裝 Swift 3。
開啟終端視窗,輸入 swift package。你會看到這個命令的介紹。你將使用這幾個命令:
- swift package init ,建立新包
- swift package update ,更新一個包的依賴
- swift package generate-xcodeproj ,為包產生 Xcode 項目
要學習 Swift 包管理器,我們將編寫一個命令列 app,用一個庫列印出所有國家的國旗字元。首先我們會建立一個可執行包。這種包也就是命令列 app。Swift web app 也屬於這種類別。
通過以下命令,建立一個名為 Flag 的可執行包:
mkdir Flagcd Flagswift package init --type executable
在運行 swift package init 命令列時,切換到 Flag 目錄是關鍵的,因為 Flag 將作為包名。你會看到輸出中會顯示一些檔案和檔案夾,它們是自動產生的。現在來熟悉一下項目結構:
https://koenig-media.raywenderlich.com/uploads/2016/12/Flag-%E2%94%9C%E2%94%80%E2%94%80-Package.swi_.png’ width= ‘300’/>
- Package.swift 包含包的描述資訊,還包含包的依賴。
- Sources/,如名稱所示,用於存放你的 Swift 源檔案。裡面會產生一個 main.swift 檔案。這是應用程式的入口。現在,它只會列印 hello world。
- Tests/ ,包含單元測試,你可以用 XCText 編寫這些測試。待會你會寫測試代碼。
回到終端視窗,運行:
swift build
這會編譯包並在 .build/debug/ 建立一個可執行檔 Flag。運行這個 app,只需要:
.build/debug/Flag
你會在螢幕上看到 Hello,world!
恭喜你!你建立和編譯通過了你的第一個 Swift 包!
建立庫
要真正能夠產生某個國家的國企字元,我們需要編寫一個名為 Atlas 的庫。然後在你的 Flag app 中調用這個庫。
切到 Flag 包之外的目錄,通過終端命令建立一個庫:
cd ..mkdir Atlascd Atlasswift package init --type library
包管理器會建立幾個檔案和檔案夾。
https://koenig-media.raywenderlich.com/uploads/2017/01/atlas-library-structure.png’ width=’300’/>
這次,main.swift 會被 Atlas.swift 所替代。這個檔案以及在 Sources/ 目錄下的檔案都會被匯入到這個庫中。實際上,庫和可執行包的區別就在於有沒有 main.swift。
這次我們需要用一個測試。用 swift test 來運行這個測試。Swift 包管理器會編譯庫並運行測試。
注意:你會在 Tests/ 目錄下看到 LinuxMain.swift 檔案,在 AtlasTests 的測試案例類中還有一個 allTests 屬性。對於要在 Linux 上運行 XCTest 測試而言,這兩者都是必須的。Linux 沒有 O-C 運行時,而 O-C 運行時會自動尋找以 test 開頭的方法並運行它。每當添加一個測試方法,你都需要修改這個 allTests 屬性,每當添加一個新的測試案例類,你都需要修改 LinuxMain.swift。
開啟 Atlas.swift 修改內容為:
public struct Country { public let code: String public init(code: String) { self.code = code.uppercased() } public var emojiFlag: String { return "\u{1f1f5}\u{1f1f7}" }}
這裡我們實現了一個 Country 結構,它通過一個 ISO 國別代碼進行初始化。emojiFlag 屬性根據國別代碼返回對應的國旗字元。現在,你需要編寫測試。
注意每個方法和屬性都標記為公有的,這樣它的每個成員對於使用這個庫的代碼來說都是課件的:]
開啟 AtlasTests.swift,編輯內容為:
import XCTest@testable import Atlasclass AtlasTests: XCTestCase { func testAustria() { XCTAssertEqual(Country(code: "AT").emojiFlag, "\u{1f1e6}\u{1f1f9}") } func testTurkey() { XCTAssertEqual(Country(code: "TR").emojiFlag, "\u{1f1f9}\u{1f1f7}") } func testUnitedStates() { XCTAssertEqual(Country(code: "US").emojiFlag, "\u{1f1fa}\u{1f1f8}") }}extension AtlasTests { static var allTests : [(String, (AtlasTests) -> () throws -> Void)] { return [ ("testAustria", testAustria), ("testTurkey", testTurkey), ("testUnitedStates", testUnitedStates) ] }}
這裡實現了 3 個測試。我們建立了 3 個不同的國籍,然後斷言它們是否擁有正確的 emoji 國旗字元。
運行測試:
swift test
你會看到 3 個測試被運行,但全部失敗。這說明我們還有很多事情要幹啊 :]
現在我們測試失敗了,我們要讓它們通過測試。
emoji 國旗的工作原理非常簡單:傳入一個國家代碼,比如 AT,然後將每個字元轉換成設為的地區指示標誌。例如, ?? 和 ??。然後將二者組合在一起,你就會得到 emoji 國旗!
https://koenig-media.raywenderlich.com/uploads/2017/01/ragecomic_flags.png’ width=’600’/>
回到 Atlas.swift 為 Country 結構增加一個方法:
func regionalIndicatorSymbol(unicodeScalar: UnicodeScalar) -> UnicodeScalar? { let uppercaseA = UnicodeScalar("A")! let regionalIndicatorSymbolA = UnicodeScalar("\u{1f1e6}")! let distance = unicodeScalar.value - uppercaseA.value return UnicodeScalar(regionalIndicatorSymbolA.value + distance)}
這裡,我們利用了字母和地區指示標誌在 Unicode 表中的值都是連續排列的原理。比如 A 是 65,B 是 66,而 ?? 是 127462,?? 就是 127463。如果將字元 P 轉換成地區指示標誌,需要計算出 A 到 P 的值相差多少,然後將 ??加上這個差值就得到 ??。
這是最難的部分。寫好這個方法後,剩下的事情就簡單了。將 emojiFlag 屬性定義修改為:
public var emojiFlag: String { return code.unicodeScalars.map { String(regionalIndicatorSymbol(unicodeScalar: $0)!) } .joined()}
我們將國家代碼的每個字母放到數組中,然後將每個字母轉換成對應的地區指示標誌,然後將它們連在一起。這就得到了國旗!
運行測試,3 個測試都通過了。
接下來將代碼提交到 Git,並標記一個本號。因為這是我們的第一個版本,我們可以將版本戳記為 1.0.0。
執行下列命令,建立 Git 存放庫並標記版本:
git initgit add .git commit -m "Initial commit"git tag 1.0.0
建立可執行包
現在你已經準備好 Flag 庫,我們可以將它添加為 Flag 可執行包的依賴。
回到 Flag 目錄,開啟 Package.swift 檔案。它目前是這個樣子:
import PackageDescriptionlet package = Package( name: "Flag")
每個 Swift 包都有一個類似的 PackageDescription。最重要的參數是 dependencies 參數。
將包描述修改為這樣:
let package = Package( name: "Flag", dependencies: [ .Package(url: "../Atlas", "1.0.0") ])
這裡,我們聲明 Flag 包擁有一個依賴,這個依賴的 URL 是 ../Atlas,版本是 1.0.0。
版本號碼應該使用語義上的版本號碼。簡單說,就是類似於 MAJOR.MINOR.PATCH 這樣的版本號碼。MAJOR 版本表示的是不向後相容的修改,MINOR 版本表示向後相容的修改,PATCH 版本是 bug 的修複。關於語義版本,細節可參考 這裡。
大部分情況下,你只想讓新版本在 bug 修複和小版本改進上做修改。幸好,Swift 包管理器允許我們這樣做。將包描述修改為:
let package = Package( name: "Flag", dependencies: [ .Package(url: "../Atlas", majorVersion: 1) ])
包管理器提供了你在更新庫時想精確控制的版本。請參考這裡。
編譯包:
swift build
Swift 包管理器將抓取、編譯庫並將之串連到你的可執行包。現在的檔案夾結構將是這個樣子:
https://koenig-media.raywenderlich.com/uploads/2017/01/flag-structure.png’ width=’300’/>
你會看到 Swift 包管理器已經細心地根據你的要求將 1.0.0 版本安裝到項目中了。開啟 main.swift 檔案,編輯內容如下:
import Atlaslet arguments = CommandLine.argumentsif arguments.count != 2 { print("USAGE: flag [iso country code]")} else { let code = arguments[1] let country = Atlas.Country(code: code) print(country.emojiFlag)}
這裡,我們匯入了 Atlas 庫,根據命令列參數的第一個參數,列印出對應的國旗 emoji。如果缺少參數,我們會列印命令協助。
編譯運行 app:
swift build./.build/debug/Flag US
你在終端視窗中看到了美國國旗!
在你和你的 app 玩得不亦樂乎的時候,我們該來打包它 了。最後編譯一下 app,這次需要加上 release 最佳化參數:
swift build --configuration release
現在你可以這樣運行你的 release 版 app 了:
./.build/release/Flag PR
你可以將 ./.build/release/Flag 檔案打包壓縮,然後分享給你的朋友、家人或其它人了 :]
用包管理器產生 Xcode 項目
老舊的命令列和文字編輯器很酷,但你可能是一個 iOS 或 macOS 程式員,你使用的是 Xcode。別擔心——大部分工作 Xcode 都可以做得很好。
回到 Atlas 包下面,產生一個 Xcode 項目:
cd ../Atlasswift package generate-xcodeproj
這會產生一個 Atlas.xcodeproj 檔案。你可以用 Xcode 開啟這個項目,像其他 Xcode 項目一樣編譯包和運行測試。
https://koenig-media.raywenderlich.com/uploads/2017/01/xcode-650x357.png’ width= ‘600’/>
同樣的方法可以用在 Flag 包上。在 Flag 檔案夾下面執行 swift package generate-xcodeproj 命令,產生 Flag.xcodeproj。
cd ../Flagswift package generate-xcodeproj
用 Xcode 開啟項目,確認 Flag 可執行目標是否選中,它顯示一個小的終端視窗的表徵圖。現在你也可以編譯和運行這個包了。
為了指定可執行命令的參數,請進入 Product\Scheme\Edit Scheme… 視窗,選擇 Run\Arguments ,在 Arguments Passed On Launch 中添加一個參數比如 US:
https://koenig-media.raywenderlich.com/uploads/2017/01/Flag_xcodeproj-650x361.png’ width=’600’/>
注意,Xcode 無法添加和編譯依賴,因此你無法完全離開命令列。
結束
你可以從這裡下載完成的 Swift 包管理器項目。
你還可以參考Swift 官網關於包管理器的章節。
關於包描述選項的最新文檔,請參考github。
關於 Swift 4 的包管理器修改方向,你可以參考 Swift 演化路線圖的郵件清單。
你還可以參考IBM 的 Swift Package Catalog,它可以協助你找到可以用在你項目中的新包。
在 Swift 包管理器能夠支援非主機平台之前,你仍然不得不使用 Cocoapods 或 Carthage 來構建 iOS、watchOS 和 tvOS app。
留作今天的作業,你可以將你的庫推到 GitHub,然後在 Atlas 中使用它的遠程依賴。提示:只需要將 dependency 的 url 參數修改為 GitHub URL。
嘗試添加新的功能,比如當沒有提供任何參數時,列出所有國家的名字和國旗。提示:你將需要用到 Locale.isoRegionCodes。
在 Atlas 庫中實現你的新功能,然後建立新的版本,比如 1.1.0,然後在 Flag 中使用這個新版本。確認你在包描述中使用了新版本,然後通過 Swift 包管理器將依賴升級到新版本。
將你的答案公布到下面的留言中,祝你開心!
Swift 包管理器教程