Getting to Know Swift Package Manager
概述
Swift Package Manager(簡(jiǎn)稱SPM)佑附,是Apple官方提供的軟件包管理器,類比如Ruby的RubyGems, Python的pip栈源。開(kāi)源框架里知名度較高的SwiftyJSON、SQLite.swift等都已支持SPM集成匹摇。
WWDC18專門開(kāi)了一個(gè)Session介紹了SPM的內(nèi)部結(jié)構(gòu)婆跑、基本使用及設(shè)計(jì)理念,并介紹了之后即將支持的一些新特性魏割。
為什么要使用SPM
最重要的一點(diǎn):官方支持
官方支持譬嚣,保證了所有Swift軟件包的可信來(lái)源和通用標(biāo)準(zhǔn)。同時(shí)钞它,SPM作為Swift項(xiàng)目的一部分拜银,Apple在Swift工具鏈中提供了更強(qiáng)大的原生支持。跨平臺(tái)構(gòu)建
SPM是一個(gè)跨平臺(tái)方案遭垛,SPM提供了完整的構(gòu)建環(huán)境尼桶,能在不同平臺(tái)上來(lái)構(gòu)建和分發(fā)Swift軟件包。和Swift更緊密的關(guān)系
SPM采用Swift編寫锯仪,可以使用Swift自身的強(qiáng)大特性疯汁,并且與Swift的核心庫(kù)緊密聯(lián)系。SPM有更先進(jìn)的設(shè)計(jì)
這個(gè)我們?cè)诤竺嬲鹿?jié)展開(kāi)卵酪。
-
SPM的社區(qū)影響范圍日益增長(zhǎng)
作為Swift開(kāi)源項(xiàng)目的一部分幌蚊,社區(qū)的熱度不斷升溫谤碳,CocoaPods的眾多核心成員也參與架構(gòu)設(shè)計(jì),影響力越來(lái)越大溢豆。
SPM基本用法
SPM的基本概念可參見(jiàn)官網(wǎng)描述蜒简。
常用命令
- swift build: 編譯Package。
- swift run: 編譯Package里的可執(zhí)行文件并運(yùn)行漩仙。
- swift test: 運(yùn)行Package里的單元測(cè)試搓茬。
- swift package: 這個(gè)命令的用法很多,Package的初始化队他、修改卷仑、更新、resolve檢查麸折、編譯選項(xiàng)指定等锡凝。
內(nèi)部結(jié)構(gòu)
對(duì)于一個(gè)Package來(lái)說(shuō),主要分為三部分垢啼,Target窜锯、Products、Dependencies.
Products
Products可以理解為Package輸出的形態(tài)芭析。對(duì)比與Cocoapods只能以靜態(tài)庫(kù)/動(dòng)態(tài)庫(kù)形式锚扎,SPM還可以輸出為可執(zhí)行文件。Target
Target理解為包含多個(gè)文件的一個(gè)模塊馁启,target之間是可以有依賴關(guān)系驾孔,同時(shí),輸出的Products也會(huì)依賴一個(gè)或多個(gè)target惯疙。Dependencies
Dependencies描述了當(dāng)前的package需要依賴其他package助币。
一個(gè)典型的Package目錄如下:
example-package-playingcard
├── Sources
│ └── DeckOfPlayingCards
│ ├── DeckOfPlayingCards.swift
│ ├── Rank.swift
│ └── Suit.swift
└── Package.swift
其中Package.swift的定義:
let package = Package(
name: "DeckOfPlayingCards",
products: [
.library(name: "DeckOfPlayingCards", targets: ["DeckOfPlayingCards"]),
],
dependencies: [
.package(url: "https://github.com/apple/example-package-fisheryates.git", from: "2.0.0"),
.package(url: "https://github.com/apple/example-package-playingcard.git", from: "3.0.0"),
],
targets: [
.target(
name: "DeckOfPlayingCards",
dependencies: ["FisherYates", "PlayingCard"]),
.testTarget(
name: "DeckOfPlayingCardsTests",
dependencies: ["DeckOfPlayingCards"]),
])
Sources目錄下的文件不需要顯示聲明,SPM會(huì)自動(dòng)導(dǎo)入螟碎,Dependencies依賴了兩個(gè)外部Package, 其中Target的依賴既有Package內(nèi)部Target的依賴也有外部Target的依賴迹栓。
Package.resolved被放在頂層包的一個(gè)文件中掉分,類似于許多軟件管理器lock的實(shí)現(xiàn),swift使用該文件來(lái)做依賴關(guān)系解析克伊。在執(zhí)行swift build swift test swift package generate-xcodeproj命令時(shí)酥郭,默認(rèn)會(huì)隱式調(diào)用swift package resolve檢查依賴版本是否正確。
Package.resolved的詳細(xì)設(shè)計(jì)思路可以參考提案愿吹。
SPM的設(shè)計(jì)思想
SPM的設(shè)計(jì)思想也遵循了Swift的一些設(shè)計(jì)思想:
安全: SPM的編譯環(huán)境是在一個(gè)獨(dú)立的沙盒里不从,無(wú)法任意執(zhí)行命令和腳本。
快速: 得益于新的llbuild構(gòu)建系統(tǒng)犁跪,SPM可以支持?jǐn)?shù)百萬(wàn)節(jié)點(diǎn)的關(guān)系依賴圖椿息。
表現(xiàn)力: 采用Swift作為SPM的配置描述語(yǔ)言歹袁。
下圖描述了Apple對(duì)SPM的大體思想:
Configuration
相對(duì)于Cocoapods等笨重啰嗦的DSL語(yǔ)法,SPM采用了Swift來(lái)作為SPM的配置描述語(yǔ)言寝优,大大降低了開(kāi)發(fā)人員的學(xué)習(xí)成本同時(shí)保留的Swift強(qiáng)大的語(yǔ)言特性条舔,并可以得到 Swift相關(guān)工具的支持。
Dependencies
SPM使用git tag來(lái)做版本依賴乏矾,格式上遵循Semantic Versioning.
Semantic Versioning(Semver): 是為了解決軟件包管理器中著名的Dependency hell問(wèn)題而設(shè)計(jì)的一套簡(jiǎn)單的規(guī)則和條件來(lái)約束版本號(hào)配置的解決方案孟抗。
Semver規(guī)定格式里分為:主版本號(hào).次版本號(hào).修訂號(hào),版本號(hào)遞增規(guī)則如下:
1. 主版本號(hào):當(dāng)你做了不兼容的 API 修改钻心。
2. 次版本號(hào):當(dāng)你做了向下兼容的功能性新增凄硼。
3. 修訂號(hào):當(dāng)你做了向下兼容的問(wèn)題修正。
Semver同時(shí)支持語(yǔ)義化關(guān)鍵字來(lái)實(shí)現(xiàn)版本控制捷沸,需要對(duì)Semver更深入了解的讀者可參考[官網(wǎng)介紹](Semantic Versioning)摊沉。下圖就是一個(gè)采用Semver版本控制的Package依賴樹(shù)結(jié)構(gòu)。
Building
SPM采用了swift-llbuild ,一個(gè)并行亿胸、高效坯钦、增量編譯的構(gòu)建系統(tǒng)作為其構(gòu)建引擎。目前Xcode9也已集成侈玄,用作對(duì)LLVM婉刀、Clang及Swift的構(gòu)建。同時(shí)序仙,因?yàn)楠?dú)立沙盒的構(gòu)建環(huán)境突颊,SPM提供了很強(qiáng)的安全性。
Workflow Features
如果你的某個(gè)軟件包還處于開(kāi)發(fā)模式下潘悼,tag的集成方式很不方便律秃,類似于Cocoapods的做法,SPM也會(huì)提供分支依賴和本地依賴做法治唤。
Tools Evolution
和Cocoapods一樣棒动,當(dāng)我們的軟件包需要依賴特定的swift版本,需要在配置描述里顯示聲明宾添。聲明的枚舉類型如下
/// Represents the version of the Swift language that should be used for
/// compiling Swift sources in the package.
public enum SwiftVersion {
case v3
case v4
case v4_2
/// User-defined value of Swift version.
///
/// The value is passed as-is to Swift compiler's `-swift-version` flag.
case version(String)
}
例如指定版本為4.0
// swift-tools-version:4.2
import PackageDescription
let package = Package(
name: "HTTPClient",
...
swiftLanguageVersions: [.v4, .v4_2]
)
SPM的未來(lái)新特性
-
與其他工具的更好的集成
使用libSwiftPM和libSyntax對(duì)SPM做更靈活的擴(kuò)展實(shí)現(xiàn)船惨。 -
發(fā)布和部署
目前的發(fā)布流程依賴于手動(dòng)建立git tag, git push origin [tag name] 的做法,之后會(huì)有更自動(dòng)化的工具實(shí)現(xiàn)缕陕。 -
支持復(fù)雜的Packages
對(duì)資源文件的支持粱锐,更復(fù)雜的編譯設(shè)置,和擴(kuò)展的編譯工具等 -
更好的管理Package生態(tài)
跨平臺(tái)的沙盒隔離扛邑、更安全的校驗(yàn)方案怜浅,支持fork操作和搜索索引等。
預(yù)見(jiàn)到等到合適的時(shí)機(jī)蔬崩,SPM成為Swift Package的標(biāo)準(zhǔn)管理工具是大勢(shì)所趨恶座。
參考:
https://developer.apple.com/videos/play/wwdc2018/411/
https://swift.org/blog/swift-package-manager-manifest-api-redesign/
https://medium.com/xcblog/apple-swift-package-manager-a-deep-dive-ebe6909a5284