什么是Swift Package Manager
Swift Package Manager(简称SPM)是苹果在2018年推出的供Swift开发者进行包管理的工具。类似于我们常使用的Cocoapods或Carthage。
那为什么苹果要推出SPM呢?它又有哪些方面的优势呢?
SPM的优势
1.导入第三方库非常简单。有可视化导入界面,按流程简单操作几步就可以完成导入。
2.定义文件简单。只需将源码放入对应的文件夹内即可,Xcode就会自动生成工程文件,并生成编译目标产物所需要的相关配置。
3.SPM是通过library或workspace方式集成的,侵入性非常低。
4.SPM多平台编译的能力非常好。UIKit一次编写即可适配iOS/iPadOS/tvOS/watchOS多个平台。
5.SPM与Cocapods兼容,可以一起使用。
下面介绍了SPM的优势,简单说下SPM、Cocoapods和Carthage的差异。
SPM、Cocoapods和Carthage差异
注:
SPM在入侵性和编译速度上有明显的优势。
根据上面说的SPM的优势,下面先尝试下制作一个简单的Package.
制作Swift Package
下面以一个简单的打印为例。
使用 SPM 搭建开发框架
$ mkdir CommandLineTool
$ cd CommandLineTool
$ swift package init --type executable
为了开发 command line tool(CLI),我们需要创建一个新的文件夹,并使用 swift package manager(SPM)来初始化项目。
最后一行的 type executable 参数将告诉 SPM,我们想创建一个 CLI,而不是一个 Framework。
创建完成后,会得到如下结构:
- Package.swift 文件:用于描述当前 Package 的信息及其依赖,需要记住的是,在 SPM 的世界里,不再有 Pod 的概念,与之对应概念是 Package,而 CLI 本身也是一个 Package
- main.swift 文件:这个文件在 Sources 目录下,它代表整个命令行工具的入口,另外记住不要更换这个文件的名字!
- Tests 文件夹:这个文件夹是用于放置测试代码的。
- .gitignore 文件:通过这个文件,git 会自动忽略 SPM 生成的 build 文件夹(.build 目录)以及 Xcode Project
将代码划分为 framework 和 executable
将源代码分成两个模块,一个是 framework 模块,一个是 executable 模块。 这样做的原因有 2 点:
- 会让测试变得更加容易
- 让你的命令行工具也可以作为其他工具依赖的 Package
具体怎么做呢?
首先,我们要保证 Sources 目录下有两个文件夹,一个用于存放 executable 相关的逻辑,一个用于存放 framework 相关的逻辑,就像下面一样:
$ cd Sources
$ mkdir CommandLineToolCore
SPM 的一个非常好的方面是,它使用文件系统作为它的处理依据,也就是说,只要采用上述操作提供的文件结构,就等于定义了两个模块。 紧接着,我们在 Package.swift 里定义了两个target, 一个是 CommandLineTool 模块,一个是 CommandLineToolCore.
// swift-tools-version: 5.6
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "CommandLineTool",
products: [
.executable(name: "CommandLineTool", targets: ["CommandLineTool"])
],
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: "5.0.0"),
.package(url: "https://github.com/Alamofire/Alamofire.git",from: "5.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 this package depends on.
.executableTarget(
name: "CommandLineTool",
dependencies: ["CommandLineToolCore"]),
.target(
name: "CommandLineToolCore",
dependencies: ["SwiftyJSON"]),
.testTarget(
name: "CommandLineToolTests",
dependencies: ["CommandLineTool"]),
]
)
通过上面这种方式,我们让 executable 模块依赖了 framework 模块。
为了能够在命令行和测试用例中方便的运行我们的代码,我们最好不要在 main.swift 中添加过多的逻辑,而是通过程序调用的方式唤起 framework 中的主逻辑。 为了实现这样的目的,我们需要创建一个名为 CommandLineTool.swift 的文件,将其放在 framework 模块中(Sources/CommandLineToolCore),它里面的内容如下:
import Foundation
public final class CommandLineTool {
private let arguments: [String]
public init(arguments: [String] = CommandLine.arguments) {
self.arguments = arguments
}
public func run() throws {
print("Hello world")
}
}
在 main.swift 中添加 run() 方法:
import CommandLineToolCore
let tool = CommandLineTool()
do {
try tool.run()
} catch {
print("Whoops! An error occurred: \(error)")
}
安装/更新依赖
一旦我们声明了新的依赖关系,只需要求 SPM 解析新的依赖关系并安装它们,然后重新生成 Xcode 项目即可。
$ swift package update
$ swift package generate-xcodeproj
在根目录下执行 swift build 吧,然后再执行 swift run 命令
$ swift build //构建swift package
$ swift run //执行swift package
> Hello world
swift package 可以用于获取package信息等,例如 swift package tools-version 可以获取到编译版本
如果接受入参,我们修改下CommandLineTool.swift文件:
import Foundation
public final class CommandLineTool {
private let arguments: [String]
public init(arguments: [String] = CommandLine.arguments) {
self.arguments = arguments
}
public func run() throws {
guard arguments.count > 1 else {
print("arguments.count小于1")
return
}
// The first argument is the execution path
let fileName = arguments[1]
print("Hello world--\(fileName)")
}
}
编写单测
我们几乎已经准备好发布这个命令行工具了,但在这样做之前,我们还是需要通过编写一些测试来确保它真正的按照预期工作。 由于我们之前将整个项目划分成了 framework 和 executable 的结果,所以测试将变得十分容易。我们所要做的就是以程序调用的方式运行它,并断言它打印入参。
首先在 Package.swift 文件中添加一个测试模块,在你的 target 数组中添加以下内容。
targets: [
.testTarget(
name: "CommandLineToolTests",
dependencies: ["CommandLineToolCore"]),
]
最后,重新生成 Xcode 项目:
$ swift package generate-xcodeproj
再次打开Xcode 项目,跳到 CommandLineToolTests.swift 中,添加以下内容:
import XCTest
import class Foundation.Bundle
import CommandLineToolCore
final class CommandLineToolTests: XCTestCase {
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
// Some of the APIs that we use below are available in macOS 10.13 and above.
guard #available(macOS 10.13, *) else {
return
}
// Mac Catalyst won't have `Process`, but it is supported for executables.
#if !targetEnvironment(macCatalyst)
let arguments = ["","你好,世界"]
let tool = CommandLineTool(arguments: arguments)
// Run the tool and assert that the file was created
try tool.run()
#endif
}
/// Returns path to the built products directory.
var productsDirectory: URL {
#if os(macOS)
for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
return bundle.bundleURL.deletingLastPathComponent()
}
fatalError("couldn't find the products directory")
#else
return Bundle.main.bundleURL
#endif
}
}
此外,还可以添加另一个测试,以验证在没有给定文件名或文件创建失败时是否抛出了正确的错误。 要运行测试,只需在命令行上运行 swift test
即可。
构建命令行工具
我们已经构建并测试了我们的命令行工具!下面开始,我们会尝试安装它,并使它能够在任何地方运行:
$ swift build -c release
$ cd .build/release
$ cp -f CommandLineTool /usr/local/bin/commandlinetool //将编译后的二进制文件移到 /usr/local/bin
$ commandlinetool hello.swift
SPM导入第三方库
下面以Alamofire为例,首先在github搜索Alamofire。
向下滑找到Swift Package Manager,复制URL
打开Xcode
选择Add Package dependency...
粘贴URL搜索
输入GitHub连接地址:github.com/Alamofire/A…
选中Add Package
稍等一会儿!
选中Add Package 下一步
这一步已经添加完成了,需要使用的类中引入import Alamofire
总结
-
SPM是苹果推出的优于Cocoapods和Carthage的第三方库管理工具.
-
SPM以可视化,简单步骤为主。
-
SPM的核心在于Pacakage.swift文件,里面是以对象为基础的管理第三方库。
-
掌握和运用SPM,让工作更加简单。