提起CocoaPods 和 Carthage , iOS的开发者应该不会陌生,作为iOS 项目开发中的模块依赖管理工具,可以自动实现依赖模块的下载,编译和链接。 Swift 社区在3.0版本以后内置了类似工具 - Swift Package Manager,有了Apple官方的支持,相信未来会大放异彩。
模块依赖
模块化带来的一个重要好处就是复用,开发者不用重复去制造已经存在的轮子,从而站在巨人的肩膀上做更有意义的事。如网络模块可以在电商应用中使用,也可以在社交应用中使用。
Swift项目中按照模块(Modules)分组,每个模块需要定义命名空间,且声明模块内的哪些类和方法允许被外部访问。
一个模块有可能已经包含运行需要的所有内容,也可能需要依赖引入第三方模块。除了系统层面的模块,如macOS中的Darwin,或Linux中的Glibc,更多的依赖是需要远程下载且按顺序编译的模块。
每个程序包(package)包括Swift源代码文件和资源配置(Manifest)文件, 每个资源配置文件命名为Package.swift,在文件内要定义包名和其他内容,且依赖于模块PackageDescription。
参看下面的例子,每个package有一到多个targets,每个target 有一到多个模块依赖。
import PackageDescription
let package = Package(
name: "dealer",
products: [
.executable(name: "Dealer", targets: ["Dealer"]),
],
dependencies: [
// 依赖
.package(url: "https://github.com/apple/example-package-deckofplayingcards.git", from: "3.0.0"),
],
targets: [
.target(
name: "Dealer",
dependencies: ["DeckOfPlayingCards"]),
]
)
注意上述案例中的products界面可以声明为executable 或library。
- library指能被其他Swift引用的模块,不能被操作系统直接运行。
- executable 指程序可以被操作系统直接运行。
模块依赖顾名思义是指模块在其他程序中被引用,依赖的内容包括相对程序的路径或绝对路径和版本号。注意模块依赖的过程是递归的,一个被依赖的模块可以有自己的依赖集合。
而模块管理就是通过工具让下载和编译依赖模块的过程自动化,降低人工管理成本。
案例
我们通过官方提供的案例来看看如何使用Swift Package Manager。
PlayingCard - library类型模块 https://github.com/apple/example-package-playingcard
FisherYates - library类型模块 https://github.com/apple/example-package-fisheryates
下载到本地可以查看文件结构:
example-package-playingcard
├── Sources
│ └── PlayingCard
│ ├── PlayingCard.swift
│ ├── Rank.swift
│ └── Suit.swift
└── Package.swift
注意 library类型模块只能被其他Swift引用,不能被操作系统直接运行。
DeckOfPlayingCards 虽然是library模块,但其代码内依赖其他library模块,https://github.com/apple/example-package-deckofplayingcards
// swift-tools-version:4.0
import PackageDescription
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"]),
]
)
Dealer - executable模块
https://github.com/apple/example-package-dealer
其依赖DeckOfPlayingCards模块,而DeckOfPlayingCards模块依赖PlayingCard 和 FisherYates 模块,但模块管理工具让开发者只需关心DeckOfPlayingCards模块即可,间接依赖工具都会自动解决。
import PackageDescription
let package = Package(
name: "dealer",
products: [
.executable(name: "Dealer", targets: ["Dealer"]),
],
dependencies: [
.package(url: "https://github.com/apple/example-package-deckofplayingcards.git", from: "3.0.0"),
],
targets: [
.target(
name: "Dealer",
dependencies: ["DeckOfPlayingCards"]),
]
)
executable模块默认地使用main.swift 作为执行程序入口。
通过命令swift build 调用编译系统生成可执行文件Dealer,位于.build/debug文件夹下。
$ swift build
$ ./.build/debug/Dealer
♠︎6
♢K
♢2
♡8
♠︎7
♣︎10
♣︎5
♢A
♡Q
♡7
推荐阅读
获取更多内容请关注微信公众号豆志昂扬:
- 直接添加公众号豆志昂扬;
- 微信扫描下图二维码;