Swift Package Manager (SPM) 包管理器
SPM(Swift Package Manager)是管理 Swift 代码分发的工具,用于处理模块代码的下载、编译和依赖关系。它类似于 CocoaPods,但更简洁,代码侵入性更小,也不需要额外安装工具。
SPM依赖安装
Xcode 自带 SPM,可以在终端上查看 SPM 版本:
$ swift package --version
Swift Package Manager - Swift 5.3.0
新建一个项目(例如 SPMTest),添加 SPM 依赖。在 Xcode 中,选择 File -> Swift Packages -> Add Package Dependency...,或者进入 PROJECT -> Swift Packages 点击加号添加依赖。
输入需要添加的第三方库的链接,点击下一步等待验证成功。根据实际需要选择版本,有三个选项:
- Version: 对应库的 Release 版本,可选择版本规则,自动下载规则内最新版本。
- Up to Next Major: 指定一个主要版本的范围,例如 5.4.2~6.0.0。
- Up to Next Minor: 指定一个小版本范围,例如 5.4.2~5.5.0。
- Range: 指定一个版本范围,例如 5.4.1~5.5.1。
- Exact: 指定一个确切的版本,例如 5.4.1。
- Branch: 直接下载某个分支的代码。
- Commit: 根据某一次提交记录的 ID 下载。
添加完成后,项目中会出现 Swift Package Dependencies 目录,这样就可以在项目中直接使用这个第三方依赖库了。要更新 SPM 中的依赖,选择 File -> Swift Packages -> Update to Latest Package Versions。如果想要修改某个第三方库的版本策略,可以双击第三方库进行修改。
创建本地Swift Package库
新建一个 Swift Package。打开项目 SPMTest,选择 File -> New -> Swift package...,把这个包命名为 ZZPackage,并添加到现有的项目中。新建完成后,在项目工程中包含了 ZZPackage 这个 Package。
如何引入 ZZPackage 到工程中并使用其中的功能模块?
在 Targets -> General -> Frameworks, Libraries, and Embedded Content 部分,点击 + 号,添加 ZZPackage。这样就把 ZZPackage 引入到项目中了。
在 ZZPackage 下的 Sources/ZZPackage 目录下新建 ExView.swift,然后在工程中使用这个文件中的方法:
import SwiftUI
extension View {
// module对外的访问权限设为public
public func printLog(_ value: Any) -> some View {
#if DEBUG
print(value)
#endif
return self
}
}
直接编译可能会报错,因为代码使用了 SwiftUI,需要 iOS 13 及以上。可以在 Package.swift 中添加 platforms: [.iOS(.v13)],或在扩展代码上面添加 @available(iOS 13.0, *)。这两种方式都可以编译成功。
发布你的 Swift Package
找到 SPMTest 文件夹下的 ZZPackage 文件夹,上传库到云端(如 GitHub、Gitee 或其他托管服务器)。然后设置 Tag 版本号就可以了。删除本地 Package,就可以通过仓库地址加载远程 Package。
发布后远程加载
打 tag 发布到 GitHub 后,在 File > Add Packages 的搜索中输入地址。如果搜索加载报错,可以换一种方式加载:创建本地 Package 关联项目,然后添加远程依赖。
- 选择
File > New > Package。 - 把新建的 Swift Package 添加到已有的项目中,Package 保存为
Library。 - 在
Targets->General->Frameworks, Libraries, and Embedded Content部分,点击+号添加Library,然后编译。 - 添加其他远程依赖库,例如添加 STNavigationController。它会自动加载依赖,加载成功后就可以在项目中使用依赖的库了。
SPM文件及配置
SPM 的主要文件和配置包括:
Source文件夹:第三方库源码位置路径文件。Package.swift:SPM 配置文件。
查看 Package.swift 文件:
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "ZZPackage",
platforms: [.iOS(.v13)],
products: [
.library(
name: "ZZPackage",
targets: ["ZZPackage"]),
],
dependencies: [],
targets: [
.target(
name: "ZZPackage",
dependencies: []),
.testTarget(
name: "ZZPackageTests",
dependencies: ["ZZPackage"]),
]
)
第一行是 Swift Tools 的版本,这里是 swift 5.3 版本。这行注释说明了构建 swift package 所需最低的 swift 版本号。然后导入了 PackageDescription,这个库提供了配置 swift package 所需的 API。
- name: 包的项目名称。
- platforms: 支持的平台及对应平台的最低版本。
- targets: 包含多个 target 的集合。指定 target 的名字为
ZZPackage,Xcode 会自动把Sources/ZZPackage目录下的所有文件添加到 package 中。如果想新建一个 target,需要在Sources/目录下新建一个文件夹,然后在 targets 数组中添加新的 target。 - products: 对外公开导出 target 产物,使得其他 target 能够使用它们。如果不写会编译报错。例如
.library(name: "ZZPackage", type: .static, targets: ["ZZPackage"]),可指定静态库或动态库,默认静态库。 - dependencies: 添加包所依赖的其他第三方 package 包的集合。例如:
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0").package(url: "https://github.com/SnapKit/SnapKit.git", from: .init(5, 0, 1)).package(url: "https://github.com/SnapKit/SnapKit.git", Package.Dependency.Requirement.branch("master")):指定分支,如果第三方不支持 SPM 可以使用这种方式。.package(path: "../ZZPackage"):关联本地的 SPM 库。
- swiftLanguageVersions: 支持的 swift 版本。
resources 添加资源文件
在 .target 下,可以添加资源文件字段 resources。例如添加图片资源自定义文件夹 imgs。如果资源或路径添加不对,会编译报错。
对于 resources 属性,有两个静态方法:process() 和 copy()。
copy()会直接拷贝,保存目录结构,可直接 copy 文件夹。process()是推荐的方式,它所配置的文件会根据具体使用的平台和内置规则进行适当的优化,但只能针对单个文件,而不能处理整个文件夹下的资源。
使用示例:
public func bgImg() -> some View {
let path = Bundle.module.path(forResource: "imgs/wechat@2x.png", ofType: nil)
guard let uiimage = UIImage.init(contentsOfFile: path!) else {
fatalError("image load path error: \(path as Any)")
}
let img = Image(uiImage: uiimage)
return self.background(img)
}
推荐使用默认文件夹 Resources。在 .target 下添加 resources,使用默认文件夹 Resources,然后在 process 中指定文件夹 Resources。使用的时候无需引用该路径,Swift 在编译的时候不会添加 Resources 路径。
public func bgImgUrl() -> some View {
let path = Bundle.module.url(forResource: "wechat@2x", withExtension: "png")
guard let data = try? Data(contentsOf: path!),
let uiimage = UIImage.init(data: data) else {
fatalError("image load path error: \(path as Any)")
}
let img = Image(uiImage: uiimage)
return self.background(img)
}
在 Assets.xcassets 中添加的图片资源不需要使用路径方式,使用方式如下:
Image("imageName", bundle: .module)
SPM 不支持混合语言开发,在同一个 target 中无法使用多语言,否则编译报错。
如何实现混合语言
可参考相关文章。这里是一个 Swift 和 OC 混编的示例:每种语言一个 target,单个 target 内不可使用多语言。对外导出的 .h 头文件,默认放在 include 文件中。如果要自定义导出文件,需要设置 publicHeadersPath 导出的公共头文件夹路径。例如,把原来的 include 文件名改为 header,然后把这个 OC 的包添加依赖到需要使用的 Swift 的包里,就可以正常使用了。