本文作者:Edmond
校对:冬瓜
CocoaPods 历险记 这个专题是 Edmond 和 冬瓜 共同撰写,对于 iOS / macOS 工程中版本管理工具 CocoaPods 的实现细节、原理、源码、实践与经验的分享记录,旨在帮助大家能够更加了解这个依赖管理工具,而不仅局限于
pod install
和pod update
。
本文知识目录
引子
在上文 「Podfile 解析逻辑」 中(建议先阅读这篇文章),我们以 Xcode 工程结构作为切入点介绍了 Podfile 背后对应的数据结构,剖析了 Podfile
文件是如何解析与加载,并最终 "入侵" 项目影响其工程结构的。今天我们来聊一聊 CocoaPods-Core[2] 中的另一个重要文件 --- Podspec
以及它所撑起的 CocoaPods 世界。
一个 Pod
的创建和发布离不开 .podspec
文件,它可以很简单也能复杂,如 QMUIKit[3](后续介绍)。
今天我们就直奔主题,来分析 Podspec
文件。
Podspec
Podspec
是用于 描述一个 Pod 库的源代码和资源将如何被打包编译成链接库或 framework 的文件 ,而 Podspec
中的这些描述内容最终将映会映射到 Specification
类中(以下简称 Spec)。
现在让我们来重新认识 Podspec
。
Podspec 初探
Podspec
支持的文件格式为 .podspec
和 .json
两种,而 .podspec
本质是 Ruby 文件。
问题来了,为什么是 JSON 格式而不像 Podfile
一样支持 YAML 呢?
笔者的理解:由于 Podspec
文件会满世界跑,它可能存在于 CocoaPods 的 CDN Service[4]、Speces Repo[5] 或者你们的私有 Specs Repo 上,因此采用 JSON 的文件在网络传输中会更友好。而 Podfile
更多的场景是用于序列化,它需要在项目中生成一份经依赖仲裁后的 Podfile
快照,用于后续的对比。
Podspec
Pod::Spec.new do |spec|
spec.name = 'Reachability'
spec.version = '3.1.0'
spec.license = { :type => 'BSD' }
spec.homepage = 'https://github.com/tonymillion/Reachability'
spec.authors = { 'Tony Million' => 'tonymillion@gmail.com' }
spec.summary = 'ARC and GCD Compatible Reachability Class for iOS and OS X.'
spec.source = { :git => 'https://github.com/tonymillion/Reachability.git', :tag => "v#{spec.version}" }
spec.source_files = 'Reachability.{h,m}'
spec.framework = 'SystemConfiguration'
end
上面这份 Reachability.podspec
配置,基本通过命令行 pod lib create NAME
就能帮我们完成。除此之外我们能做的更多,比如,默认情况下 CococaPods 会为每个 Pod
framework 生成一个对应的 modulemap
文件,它将包含 Podspec
中指定的公共 headers。如果需要自定义引入的 header 文件,仅需配置 moduel_map
即可完成。
下面是进阶版配置:
Pod::Spec.new do |spec|
spec.name = 'Reachability'
# 省略与前面相同部分的配置 ...
spec.module_name = 'Rich'
spec.swift_version = '4.0'
spec.ios.deployment_target = '9.0'
spec.osx.deployment_target = '10.10'
spec.source_files = 'Reachability/common/*.swift'
spec.ios.source_files = 'Reachability/ios/*.swift', 'Reachability/extensions/*.swift'
spec.osx.source_files = 'Reachability/osx/*.swift'
spec.framework = 'SystemConfiguration'
spec.ios.framework = 'UIKit'
spec.osx.framework = 'AppKit'
spec.dependency 'SomeOtherPod'
end
像 ???? 我们为不同的系统指定了不同的源码和依赖等,当然可配置的不只这些。
Podspec
支持的完整配置分类如下:
想了解更多的配置选项:传送门[6]。
Convention Over Configuration
说到配置,不得不提一下 CoC
约定大于配置。约定大于配置算是在软件工程较早出现的概念的了,大意是:为了简单起见,我们的代码需要按照一定的约定来编写(如代码放在什么目录,用什么文件名,用什么类名等)。这样既简化了配置文件,同时也降低了学习成本。
约定大于配置可以说是通过 Ruby on Rails[7] 发扬光大的。尽管它一直饱受争议,但是主流语言的依赖管理工具,如 Maven
、npm
等都遵循 CoC
进行不断演进的,因为 CoC
能够有效帮助开发者减轻选择的痛感,减少无意义的选择。一些新的语言也吸收了这个思想,比如 Go 语言。如果用 C/C++ 可能需要定义复杂的 Makefile 来定义编译的规则,以及如何运行测试用例,而在 Go 中这些都是约定好的。
举个 ???? :Podfile
中是可以指定 pod library 所链接的 Xcode project,不过大多情况下无需配置,Cocoa