Swift Package Manager 是苹果推出的用于创建使用swift的库和可执行程序的工具。
SwiftPM有什么作用?
能够通过命令快速创建library或者可执行的swift程序,能够跨平台使用,能够使开发出来的项目能够在不同平台上运行。
SwiftPM有哪些局限?
1、目前只能用来写跨平台的项目,如swift服务端开发,现在的Vapor、Perfect等服务端的web框架均使用SwiftPM来构建和管理依赖。
2、iOS和MacOS目前还未支持,但是后续一定会支持,现在只需要耐心的等待。
注意:本文适用于Swift 4.1.0 版本,后续会有更新,如果里面的demo不能正常运行,请检查版本。
那么,让我门开始吧
-
创建一个Package
1、创建文件夹,并进入文件夹
$ mkdir Hello
$ cd Hello
2、初始化一个名为Hello的package
$ swift package init
会生成以下的文件结构
3、使用命令行编译项目
$ swift build
Compile Swift Module 'Hello' (1 sources)
使用命令行运行test
$ swift test
Compile Swift Module 'HelloTests' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloPackageTests.xctest/Contents/MacOS/HelloPackageTests
Test Suite 'All tests' started at 2018-08-28 11:23:16.755
Test Suite 'HelloPackageTests.xctest' started at 2018-08-28 11:23:16.755
Test Suite 'HelloTests' started at 2018-08-28 11:23:16.755
Test Case '-[HelloTests.HelloTests testExample]' started.
Test Case '-[HelloTests.HelloTests testExample]' passed (0.264 seconds).
Test Suite 'HelloTests' passed at 2018-08-28 11:23:17.019.
Executed 1 test, with 0 failures (0 unexpected) in 0.264 (0.264) seconds
Test Suite 'HelloPackageTests.xctest' passed at 2018-08-28 11:23:17.019.
Executed 1 test, with 0 failures (0 unexpected) in 0.264 (0.264) seconds
Test Suite 'All tests' passed at 2018-08-28 11:23:17.019.
Executed 1 test, with 0 failures (0 unexpected) in 0.264 (0.264) seconds
-
创建一个可执行项目
1、创建文件夹,并进入文件夹
$ mkdir HelloExcutable
$ cd HelloExcutable/
2、初始化一个名为Hello的可执行文件
$ swift package init --type executable
会生成以下的文件结构
3、运行HelloExcutable
$ swift run HelloExcutable
Compile Swift Module 'HelloExcutable' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/HelloExcutable
Hello, world!
4、Build HelloExcutable
$ swift build
会在当前目录创建一个.build的文件夹,里面是编译后的内容。
可以看到在x86_64-apple-macosx10.10/debug文件夹里有一个HelloExcutable的可执行文件。
5、直接运行HelloExcutable
$ .build/x86_64-apple-macosx10.10/debug/HelloExcutable
6、多个xxx.swift的情况:
在HelloExcutable(和Main.swift同级)的目录下创建名为Greeter.swift的文件,在里面插入代码:
func sayHello(name: String) {
print("Hello, \(name)!")
}
修改Main.swift:
if CommandLine.arguments.count != 2 {
print("Usage: hello NAME")
} else {
let name = CommandLine.arguments[1]
sayHello(name: name)
}
7、运行来查看效果
$ swift run HelloExcutable `whoami`
Compile Swift Module 'HelloExcutable' (2 sources)
Linking /Users/leacode/Documents/Swift/HelloExcutable/.build/x86_64-apple-macosx10.10/debug/HelloExcutable
Hello, leacode!
会获得命令行的结果,并打印出来
实际项目种使用
-
创建项目
执行命令
$ swift package init --help
OVERVIEW: Initialize a new package
OPTIONS:
--type empty|library|executable|system-module
可以看到目前SwiftPM支持四种类型,指定不同的type可以创建不同类型的项目。由于swift支持不同的平台,所以创建项目的时候并没有生成xcode文件,如果是在非Mac平台开发,可以使用其他IDE进行开发,如果在Mac上开发就会方便很多,可以使用以下命令创建xcodeproj文件:
$ swift package generate-xcodeproj
-
编译项目
执行命令
$ swift build --help
OVERVIEW: Build sources into binary products
USAGE: swift build [options]
OPTIONS:
--build-path Specify build/cache directory [default: ./.build]
--build-tests Build both source and test targets
--configuration, -c Build with configuration (debug|release) [default: debug]
--disable-prefetching
--disable-sandbox Disable using the sandbox when executing subprocesses
--enable-build-manifest-caching
Enable llbuild manifest caching [Experimental]
--no-static-swift-stdlib
Do not link Swift stdlib statically
--package-path Change working directory before any other operation
--product Build the specified product
--show-bin-path Print the binary output path
--static-swift-stdlib Link Swift stdlib statically
--target Build the specified target
--verbose, -v Increase verbosity of informational output
-Xcc Pass flag through to all C compiler invocations
-Xcxx Pass flag through to all C++ compiler invocations
-Xlinker Pass flag through to all linker invocations
-Xswiftc Pass flag through to all Swift compiler invocations
--help Display available options
可以看到build项目除了直接用build命令之外还可以加上一些额外的选项。这里做一下讲解
USAGE: swift build [options]
OPTIONS:
--build-path 指定编译文件存放的路径(默认路径是./.build)
--build-tests 编译源码和测试代码
--configuration, -c 编译环境(debug|release),默认是debug
--disable-prefetching 禁止prefetching
--disable-sandbox 禁用沙盒
--enable-build-manifest-caching
打开llbuild清单缓存(实验功能,这个就是增量编译了)
--no-static-swift-stdlib 不要静态link Swift stdlib
--package-path 当自己指定源代码路径的时候使用此命令
--product 编译指定的product
--show-bin-path 打印二进制文件输出路径
--static-swift-stdlib 静态link Swift stdlib
--target 编译特定的target
--verbose, -v Increase verbosity of informational output
-Xcc 将标志传递给所有C编译器调用
-Xcxx 将标志传递给所有C++编译器调用
-Xlinker 将标志传递给所有linker调用
-Xswiftc 将标志传递给所有Swift编译器调用
--help 查看帮助
如果你只是一个swift开发人员,指定路径、指定环境是工作中会最常用到的命令。
-
运行项目
执行命令
$ swift run --help
OVERVIEW: Build and run an executable product
USAGE: swift run [options] [executable [arguments ...]]
OPTIONS:
--build-path Specify build/cache directory [default: ./.build]
--configuration, -c Build with configuration (debug|release) [default: debug]
--disable-prefetching
--disable-sandbox Disable using the sandbox when executing subprocesses
--enable-build-manifest-caching
Enable llbuild manifest caching [Experimental]
--no-static-swift-stdlib
Do not link Swift stdlib statically
--package-path Change working directory before any other operation
--skip-build Skip building the executable product
--static-swift-stdlib Link Swift stdlib statically
--verbose, -v Increase verbosity of informational output
-Xcc Pass flag through to all C compiler invocations
-Xcxx Pass flag through to all C++ compiler invocations
-Xlinker Pass flag through to all linker invocations
-Xswiftc Pass flag through to all Swift compiler invocations
--help Display available options
POSITIONAL ARGUMENTS:
executable The executable to run
和上面的build一样,在运行项目的时候,可以配置不同的环境参数来运行不同的target或环境
-
添加依赖
通过SwiftPM创建项目的时候会在项目的根目录生成一个Package.swift的文件,这个文件就相当于cocoapods的Podfile 或者Carthage 的Cartfile.
首先来看看 HelloExcutable 这个项目的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: "HelloExcutable",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.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: "HelloExcutable",
dependencies: []),
]
)
目前这个项目没有添加任何依赖,添加依赖的步骤如下:
1、引入一个依赖库SwiftNIO
import PackageDescription
let package = Package(
name: "HelloExcutable",
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "1.9.2")
],
targets: [
.target(
name: "HelloExcutable",
dependencies: ["NIO", "NIOHTTP1", "NIOFoundationCompat"]),
]
)
2、执行命令
swift build
这时候会下载依赖的package,下载完后会编译项目。
3、重新生成 xcodeproj文件(仅Mac适用)
swift package generate-xcodeproj
打开生成的 HelloExcutable.xcodeproj 文件就可以看到刚刚添加的依赖已经集成到项目里了:
Package.swift怎么写?
Package.swift是用于管理项目依赖以及项目结构的文件,文件内容就是一个 Package 类的实例。
Package
这里是Package这个Class的init方法:
final public class Package {
...
public init(name: String, //项目的名称
pkgConfig: String? = default,
providers: [PackageDescription.SystemPackageProvider]? = default,
products: [PackageDescription.Product] = default, // 对外公开的产物
dependencies: [PackageDescription.Package.Dependency] = default, // 依赖
targets: [PackageDescription.Target] = default, // 项目的targets
swiftLanguageVersions: [Int]? = default, // swift版本
cLanguageStandard: PackageDescription.CLanguageStandard? = default, // c语言标准
cxxLanguageStandard: PackageDescription.CXXLanguageStandard? = default) // c++语言标准
}
可以根据项目的需要,设置对应的参数。 比如要用SwiftPM做一个framework,那么需要设置products指定Framework的名字和target。
Package.Dependency
当项目中需要添加依赖的时候,需要设置dependencies参数, 是一个Package.Dependency类的集合,下面是Package.Dependency的部分源码:
extension Package.Dependency : Equatable {
public static func package(url: String, from version: PackageDescription.Version) -> PackageDescription.Package.Dependency
public static func package(url: String, _ requirement: PackageDescription.Package.Dependency.Requirement) -> PackageDescription.Package.Dependency
public static func package(url: String, _ range: Range<PackageDescription.Version>) -> PackageDescription.Package.Dependency
public static func package(url: String, _ range: ClosedRange<PackageDescription.Version>) -> PackageDescription.Package.Dependency
...
}
类似cocoapods 和 Carthage, Package.Dependency有两个参数,第一个参数设置依赖库的url,第二个参数设置依赖库的版本
设置版本的语法:
.package(url:"", from: "1.0.0") (1.0.0 ..< 2.0.0)
.package(url:"", from: "1.2.0") (1.2.0 ..< 2.0.0)
.package(url:"", from: "1.5.8") (1.5.8 ..< 2.0.0)
.package(url:"", .exactItem(Version(stringLiteral: "1.2.0")) (==1.2.0)
.package(url:"", .exactItem(Version(stringLiteral: "1.2.0")) (==1.2.0)
.package(url:"", .revisionItem("74663ec")) 某次提交的revision的值
.package(url:"", .branchItem("develop")) 分支名
.package(url:"", .localPackageItem) 本地依赖
.package(url:"", Version(stringLiteral: "1.2.3")...Version(stringLiteral: "1.2.8")) (>=1.2.3 && <=1.2.8)
.package(url:"", Version(stringLiteral: "1.2.3")..<Version(stringLiteral: "1.2.8")) (>=1.2.3 && <1.2.8)
同样上面的Version也可以用以下方式来写:
Version(1, 2, 0) 相当于于 Version(stringLiteral: "1.2.0")
Version(1, 0, 0)..<Version(1, .max, .max) 意思是版本大于1.0.0 小于2.0.0
Target
另外一个比较重要的类是Target:
final public class Package {
...
public static func target(name: String, // target的名称
dependencies: [PackageDescription.Target.Dependency] = default, // target 的依赖,这里面主要指定Package添加的依赖的module的名字
path: String? = default, // target的路径,如果自定义文件夹需要设置此参数
exclude: [String] = default, // target path中不希望被包含的path
sources: [String]? = default, // 资源文件的路径
publicHeadersPath: String? = default // 公共header文件的路径
) -> PackageDescription.Target
...
}
用于定义项目里的target。
本篇主要介绍了SwiftPM的一些基础用法,使用时需要注意以下几点:
1、dependencies里面的链接和版本一定要写对,target里的dependencies对应的总的dependencies中的module,一个依赖可以有多个module。
2、注意多个库依赖时的兼容性,如果出现卡着不动的时候,常常是依赖的版本有问题,可以逐步添加来排查问题。
3、Package.swift中的语法不要写错,注意 [ ]和 ""要成对存在,不要漏了前后的符号。
4、如果你不是在Mac上开发,可以使用Atom等支持高亮的编辑器来编辑Package.swift文件。
5、不要去尝试用它来管理iOS项目的依赖,现在还不支持,到支持的时候我会更新此文章。
希望本文能给你带来一些帮助,有疑问或者需要补充的地方欢迎留言。