Swift Package Manager使用總結

Glen2017發表於2018-01-03

一、簡介

Swift Package Manager(以下簡稱SPM)是蘋果在swift3.0中加入的一個包管理工具,用於處理模組程式碼的下載、編譯和依賴關係等。跟CocoaPods和Carthage功能類似,不過比這兩個更簡潔,程式碼的侵入性更小,也不需要額外安裝工具。

需要注意的是,SPM在swift4.0中重新設計了API,所以你需要確定你當前使用的swift版本,否則很可能會出現build失敗的情況,以下程式碼都是基於swift4.0版本。

二、使用教程

既然是包管理,所以我們的操作都是以包的形式,依賴的第三方庫都會當成一個個單獨的包來處理,依賴包可以巢狀。

SPM包含兩種包:可執行的二進位制包(executable)和靜態庫包(Library),兩者唯一的區別就是前者會生成二進位制可執行檔案,可以直接用命令列執行。如果建立的是Library,用命令列執行會提示沒有可執行檔案,這個時候只需要在Sources/目錄下建立一個main.swift檔案就可以執行了。

1、通過命令建立包:

$ mkdir SPMDemo    
$ cd SPMDemo         
$ swift package init --type executable(or library)  //初始化包,不帶引數的話預設是Library
Creating executable package: SPMDemo
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/main.swift
Creating Tests/
$ swift build    //編譯並生成可執行的二進位制檔案
Compile Swift Module 'SPMDemo' (1 sources)
Linking ./.build/debug/SPMDemo
$ swift run 	//執行生成的檔案
Hello, world!   //執行效果
複製程式碼

2、加入依賴包

我們需要在前面建立的包中使用第三方庫(這裡以SwiftyJSON為例),編輯Package.swift檔案:

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

import PackageDescription

let package = Package(
    name: "SPMDemo",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),	//第三方庫url和版本號
    ],
    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 which this package depends on.
        .target(
            name: "SPMDemo",
            dependencies: ["SwiftyJSON"]),	//依賴的包名稱
    ]
)
複製程式碼

注:關於dependencies的格式定義可以看這裡有詳細說明文件,可以指定分支、具體的commit編號、版本範圍等等。

然後我們檢驗一下引入的依賴包是否可用,編輯Sources目錄下的main.swift檔案:

import SwiftyJSON	//引入模組

let json = JSON(["name":"Jack", "age": 25])
print(json)

print("Hello, world!")
複製程式碼

儲存之後我們再run一下:

$ swift run
Fetching https://github.com/SwiftyJSON/SwiftyJSON.git
Cloning https://github.com/SwiftyJSON/SwiftyJSON.git
Resolving https://github.com/SwiftyJSON/SwiftyJSON.git at 4.0.0
Compile Swift Module 'SwiftyJSON' (1 sources)
Compile Swift Module 'SPMDemo' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/SPMDemo
{
  "name" : "Jack",
  "age" : 25
}
Hello, world!
複製程式碼

可以看到輸出了我們的json結構體,說明引入依賴包成功了!

注:第一次run的時候SPM會先將依賴的庫克隆下來並編譯好放在.build隱藏資料夾中,如果把這個資料夾刪除重新run,會重新下載。

3、現在我們來試試自己定義一個庫作為其他專案的依賴包

$ mkdir MyLib    
$ cd MyLib         
$ swift package init	//初始化包,不帶引數的話預設是Library
Creating library package: MyLib
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/MyLib/MyLib.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/MyLibTests/
Creating Tests/MyLibTests/MyLibTests.swift
複製程式碼

建立Library的話預設是沒有main.swift檔案的,Sources目錄下只有一個MyLib.swift檔案,它給我們定義了一個結構體,但並不是public的,我們知道swift中只有被public和open修飾才能被其他模組訪問,所以我們把它改成public:

public struct MyLib {
    var text = "Hello, MyLib!"
    public var num:Int
    public init() {
        num = 2
    }
}
複製程式碼

然後我們build一下,確保我們的Library能順利編譯通過。

因為SPM依賴包必須使用git url和版本號,所以我們需要為我們的庫建立一個git倉庫並提交程式碼和打tag:

$ git init
$ git add .
$ git commit -m "Initial Commit"
$ git tag 1.0.0
複製程式碼

接下來修改前面的Package.swift檔案,加入我們的Library:

    dependencies: [
        // Dependencies declare other packages that this package depends on.
        .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"),
        .package(url: "./MyLib", from: "1.0.0")    //因為沒有推送到遠端倉庫,所以這裡用相對路徑,from就是我們剛才打的tag
    ],
    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 which this package depends on.
        .target(
            name: "SPMDemo",
            dependencies: ["SwiftyJSON","MyLib"]),      //新增依賴包名
    ]

複製程式碼

然後我們再檢驗一下依賴包是否可用,編輯main.swift檔案:

import SwiftyJSON
import MyLib

//SwiftyJSON
let json = JSON(["name":"Jack", "age": 25])
print(json)

//自定義庫
let lib = MyLib()
print(lib)
複製程式碼

然後我們再run一次看看:

{
  "name" : "Jack",
  "age" : 25
}
MyLib(text: "Hello, MyLib!”, num: 2)
複製程式碼

結果輸出了我們自定義的庫中結構體的內容,說明我們引入自定義依賴包成功了!

三、如何在iOS工程中使用SPM

除了macOS以外,目前SPM不支援蘋果其他平臺,所以如果想替換Pods或者Carthage還是謹慎一點,等蘋果完全支援iOS以後再切換過來吧。不過我們可以以引入子工程的方式在iOS專案中使用SPM,具體步驟如下:

1、建立新的iOS專案或者使用已存在的專案

2、將該工程初始化為SPM(swift package init)

3、建立一個空的依賴原始檔和路徑,因為建立xcodeproj檔案的時候需要至少一個原始檔,這裡我在dep-sources路徑下建立一個Dependencies.swift檔案:

$ mkdir dep-sources
$ cd dep-sources/
$ touch Dependencies.swift

複製程式碼

4、修改Package.swift檔案,加入依賴庫並設定target為新的名字,否則會跟iOS工程的target重名,這裡我改為Dependencies,然後在target中設定第3步的路徑

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

import PackageDescription

let package = Package(
    name: "Dependencies",
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "Dependencies",
            type: .static,
            targets: ["Dependencies"]),
    ],
    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"),
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.6.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 which this package depends on.
        .target(
            name: "Dependencies",
            dependencies: ["SwiftyJSON","Alamofire"],
            path: "dep-sources" //原始檔路徑
        )
    ]
)

複製程式碼

5、執行swift package generate-xcodeproj命令,這裡會生成Dependencies.xcodeproj

$ swift package generate-xcodeproj
Fetching https://github.com/SwiftyJSON/SwiftyJSON.git
Fetching https://github.com/Alamofire/Alamofire.git
Cloning https://github.com/SwiftyJSON/SwiftyJSON.git
Resolving https://github.com/SwiftyJSON/SwiftyJSON.git at 4.0.0
Cloning https://github.com/Alamofire/Alamofire.git
Resolving https://github.com/Alamofire/Alamofire.git at 4.6.0
generated: ./Dependencies.xcodeproj
複製程式碼

6、開啟iOS工程,將第5步生成的project拖入工程作為sub-project,然後新增依賴的framework就可以了(xcode9經常莫名其妙抽風,建議做完後關閉重新開啟工程)

Swift Package Manager使用總結

7、驗證引入包是否成功

四、可能碰到的問題

1、no such module

$ swift build
Compile Swift Module 'SPMDemo' (1 sources)
/SPMDemo/Sources/SPMDemo/main.swift:2:8: error: no such module 'MyLib'
import MyLib

複製程式碼

這有可能是你只加入了依賴包的路徑而沒有在target中加入模組名稱

    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 which this package depends on.
        .target(
            name: "SPMDemo",
            dependencies: ["SwiftyJSON"]),      //缺少“MyLib”
    ]
複製程式碼

還有一種情況是更新了依賴包,但是沒有構建到.build目錄下,這個時候只要把.build檔案刪除,重新構建一次就可以了。有什麼問題歡迎留言討論!

相關文章