Podfile是什么
Podfile是CocoaPods使用的配置文件,它定义了项目依赖的库,以及库的版本、来源等信息。 通过Podfile来管理第三方库, 。
Podfile解析
一个简单的Podfile
target 'MyApp' do //向名为 MyApp 的target做一些配置
pod 'AFNetworking', '~> 3.0' //引入 AFNetworking库, 版本 >=3.0. <4.0
end
~> 3.0 这种写法叫做 乐观版本锁定(optimistic version locking) , 既能使用更新的 bug 修复版本,又能避免破坏性变更(如重大 API 变更)。
指定pod库的版本
如果指定版本,默认就是最新的。
0.1 0.1版本
> 0.1 高于0.1版本(不包含0.1版本)的任意一个版本
>= 0.1 高于0.1版本(包含0.1版本)的任意一个版本
< 0.1 低于0.1版本(不包含0.1版本)的任意一个
<= 0.1 低于0.1版本(包含0.1版本)的任意一个
~> 0.1.2 版本 0.1.2的版本到0.2 ,不包括0.2。这个基于你指定的版本号的最后一个部分。这个例子等效于>= 0.1.2并且 <0.2.0,并且始终是你指定范围内的最新版本。
一个复杂些的Podfile
# 下面两行是指明依赖库的来源地址
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/Artsy/Specs.git'
# 说明平台是ios,版本是9.0
platform :ios, '9.0'
# 忽略引入库的所有警告(强迫症者的福音啊)
inhibit_all_warnings!
# 针对MyApp target引入AFNetworking
target 'MyApp' do
pod 'AFNetworking', '~> 3.0'
target 'MyAppTests' do # 针对MyAppTests target引入OCMock,
inherit! :search_paths
pod 'OCMock', '~> 2.0.1'
end
end
# 这个是cocoapods的一些配置,官网并没有太详细的说明,一般采取默认就好了,也就是不写.
post_install do |installer|
installer.pods_project.targets.each do |target|
puts target.name
end
end
Build configurations (编译配置)
默认情况下, 依赖项会被安装在所有target的build configuration中。为了调试或者处于其他原因,依赖项只能在给定的build configuration中被启用。
pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
或者,可以弄白名单只指定一个build configurations。
pod 'PonyDebugger', :configuration => 'Debug'
注意:默认情况下如果不指定具体生成配置,那么会包含在所有的配置中,如果你想具体指定就必须手动指明。
引入库的几种方式
引入库的子模块
// 安装QueryKit库的所有内容
pod 'QueryKit'
// 仅安装QueryKit库下的Attribute模块
pod 'QueryKit/Attribute'
// 仅安装QueryKit下的Attribute和QuerySet模块
pod 'QueryKit', :subspecs => ['Attribute', 'QuerySet']
引入本地库
我们也可以指定依赖库的来源地址。如果我们想引入我们本地的一个库,可以这样写:
pod 'AFNetworking', :path => '~/Documents/AFNetworking'
使用这个选项后,`Cocoapods`会将给定的文件夹认为是Pod的源,并且在工程中直接引用这些文件。这就意味着你编辑的部分可以保留在`CocoaPods`安装中,如果我们更新本地`AFNetworking`里面的代码,`cocoapods`也会自动更新。
被引用的文件夹可以来自你喜爱的[SCM](https://so.csdn.net/so/search?q=SCM&spm=1001.2101.3001.7020),甚至当前仓库的一个git子模块
注意:Pod的`podspec`文件也应该被放在这个文件夹当中
引入指定的git仓库的库
需要特别注意的是,虽然这样将会满足任何在Pod中的依赖项通过其他Pods 但是podspec必须存在于仓库的根目录中。
引入master分支(默认)
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git'
引入指定的分支
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :branch => 'dev'
引入某个节点的代码
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :tag => '0.7.0'
引入某个特殊的提交节点
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :commit => '082f8319af'
podspec引入
podspec可以从另一个源库的地址引入
pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec'
podspec
使用给定podspec文件中定义的代码库的依赖关系。如果没有传入任何参数,podspec优先使用根目录,如果是其他情况必须在后面指明。(一般使用默认设置即可)例如
# 不指定表示使用根目录下的podspec,默认一般都会放在根目录下
podspec
# 如果podspec的名字与库名不一样,可以通过这样来指定
podspec :name => 'QuickDialog'
# 如果podspec不是在根目录下,那么可以通过:path来指定路径
podspec :path => '/Documents/PrettyKit/PrettyKit.podspec'
target
在给定的块内定义pod的target(Xcode工程中的target)和指定依赖的范围。一个target应该与Xcode工程的target有关联。默认情况下,target会包含定义在块外的依赖,除非指定不使用inherit!来继承(说的是嵌套的块里的继承问题)
定义一个简单target ZipApp引入SSZipArchive库
target 'ZipApp' do
pod 'SSZipArchive'
end
定义一个ZipApptarget仅引入SSZipArchive库,定义ZipAppTeststarget引入Nimble的同时也会继承ZipApptarget里面的SSZipArchive库
target 'ZipApp' do
pod 'SSZipArchive'
target 'ZipAppTests' do
inherit! :search_paths
pod 'Nimble'
end
end
target块中嵌套多个子块
target 'ShowsApp' do
# ShowsApp 仅仅引入ShowsKit
pod 'ShowsKit'
# 引入 ShowsKit 和 ShowTVAuth
target 'ShowsTV' do
pod 'ShowTVAuth'
end
# 引入了Specta和Expecta以及ShowsKit
target 'ShowsTests' do
inherit! :search_paths
pod 'Specta'
pod 'Expecta'
end
end
抽象target
定义一个新的抽象目标,它可以方便的用于目标依赖继承。
简单写法
abstract_target 'Networking' do
pod 'AlamoFire'
target 'Networking App 1'
target 'Networking App 2'
end
定义一种abstract_target包含多个target
# 注意:这是个抽象的target也就是说在工程中并没有这个target引入ShowsKit
abstract_target 'Shows' do
pod 'ShowsKit'
# ShowsiOS target会引入ShowWebAuth库以及继承自Shows的ShowsKit库
target 'ShowsiOS' do
pod 'ShowWebAuth'
end
# ShowsTV target会引入ShowTVAuth库以及继承自Shows的ShowsKit库
target 'ShowsTV' do
pod 'ShowTVAuth'
end
# ShowsTests target引入了Specta和Expecta库,并且指明继承Shows,所以也会引入ShowsKit
target 'ShowsTests' do
inherit! :search_paths
pod 'Specta'
pod 'Expecta'
end
end
platform 指定平台和最低部署目标
platform用于指定应建立的静态库的平台。CocoaPods提供了默认的平台版本配置:
- iOS->4.3
- OS X->10.6
- tvOS->9.0
- watchOS->2.0
如果部署目标需要iOS < 4.3,armv6体系结构将被添加到ARCHS。
#指定具体平台和版本
platform :ios, '4.0'
platform :iOS
project
如果没有显示的project被指定,那么会默认使用target的父target指定的project作为目标。如果如果没有任何一个target指定目标,那么就会使用和Podefile在同一目录下的project。同样也能够指定是否这些设置在release或者debug模式下生效。为了做到这一点,你必须指定一个名字和:release/:debuge关联起来
# MyGPSApp这个target引入的库只能在FastGPS工程中引用
target 'MyGPSApp' do
project 'FastGPS'
...
end
# 原理同上
target 'MyNotesApp' do
project 'FastNotes'
...
end
使用自定义的编译配置
project 'TestProject', 'Mac App Store' => :release, 'Test' => :debug
inhibit_all_warnings!(强迫症者的福音)
inhibit_all_warnings!屏蔽所有来自于cocoapods依赖库的警告。你可以全局定义,也能在子target里面定义,也可以指定某一个库:
# 隐藏SSZipArchive的警告而不隐藏ShowTVAuth的警告
pod 'SSZipArchive', :inhibit_warnings => true
pod 'ShowTVAuth', :inhibit_warnings => false
use_frameworks! 告知 CocoaPods 为所有 Pods 创建动态框架而不是静态库,这对于 Swift 项目是必需的
通过指定use_frameworks!要求生成的是framework而不是静态库。
如果使用use_frameworks!命令会在Pods工程下的Frameworks目录下生成依赖库的framework
如果不使用use_frameworks!命令会在Pods工程下的Products目录下生成.a的静态库
Workspace
默认情况下,我们不需要指定,直接使用与Podfile所在目录的工程名一样就可以了。如果要指定另外的名称,而不是使用工程的名称,可以这样指定:
workspace 'MyWorkspace'
source
source是指定pod的来源。如果不指定source,默认是使用CocoaPods官方的source。(建议使用默认设置)
CocoaPods Master Repository
# 使用其他来源地址
source 'https://github.com/artsy/Specs.git'
# 使用官方默认地址(默认)
source 'https://github.com/CocoaPods/Specs.git'
关于作用,举个例子方便理解:
source 'MyGit'
source '官方cocoapods.org'
//项目中看到如下pod安装
pod 'ABCD'
//但是我在cocoapods.org并没有搜到ABCD的库。
实际上ABCD来源于MyGit。
pod 'ABCD' 会安装source声明的地址,先从MyGit中查找ABCD库,如果有则安装。
没有则在官方cocoapods.org查找ABCD库安装。
Hooks
Podfile提供了hook机制,它将在安装过程中调用。hook是全局性的,不存储于每个target中。
plugin
指定应在安装期间使用的插件。使用此方法指定应在安装期间使用的插件,以及当它被调用时,应传递给插件的选项。例如:
# 指定在安装期间使用cocoapods-keys和slather这两个插件
plugin 'cocoapods-keys', :keyring => 'Eidolon'
plugin 'slather'
pre-install 下载完成,但是还没有安装
当我们下载完成,但是还没有安装之时,可以使用hook机制通过pre_install指定要做更改,更改完之后进入安装阶段。
pre_install do |installer|
# 做一些安装之前的更改
end
post_install 安装完成,但还没有写入磁盘
当我们安装完成,但是生成的工程还没有写入磁盘之时,我们可以指定要执行的操作。
比如,我们可以在写入磁盘之前,修改一些工程的配置:
post_install do |installer| installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['GCC_ENABLE_OBJC_GC'] = 'supported'
end
end
end
def
我们还可以通过def命令来声明一个pod集:
def 'CustomPods'
pod 'IQKeyboardManagerSwift'
end
然后,我们就可以在需要引入的target处引入:
//这么写的好处是:如果有多个`target`,而不同`target`之间并不全包含,那么可以通过这种方式来分开引入。
target 'MyTarget' do
CustomPods
end
例如
def shared_pods
pod 'Alamofire', '~> 5.4'
end
target 'YourAppTarget' do
shared_pods
end
target 'YourTestTarget' do
shared_pods
end
常用pod命令
1. pod init
- 作用:在当前目录下创建一个新的
Podfile文件,该文件用于指定项目的依赖。 - 使用场景:当你刚开始一个项目并希望开始使用 CocoaPods 管理依赖时。
2. pod install
- 作用:根据
Podfile的描述安装或更新所有列出的 Pods,并为项目生成.xcworkspace文件(如果尚不存在)。 - 重要提示:一旦你运行了
pod install并生成了.xcworkspace文件,你应该总是通过打开这个.xcworkspace文件而不是原始的.xcodeproj文件来工作,以确保所有的 Pod 都被正确加载。 - 使用场景:首次添加依赖或者有新的依赖加入到
Podfile中时
3. pod update
- 作用:更新所有 Pods 到
Podfile中指定的最新版本(如果没有指定版本,则更新至最新稳定版)。这会忽略现有的Podfile.lock文件中的版本锁定。 - 使用场景:当需要将所有依赖升级到最新版本时(注意:这可能会引入不兼容的变化)。
4. pod repo update
- 作用:更新本地 CocoaPods 主仓库索引。这是从互联网上获取最新的 Pod 规格信息的过程。
- 使用场景:在执行
pod install或pod update之前,如果你想要确保你的本地仓库是最新的,可以先运行此命令。
8. pod deintegrate
- 作用:移除 CocoaPods 集成,包括删除
.xcworkspace文件、Podfile和Pods目录等。 - 使用场景:当你决定不再使用 CocoaPods 来管理依赖时
9. pod cache clean --all
- 作用:清除 CocoaPods 的缓存。有时候清理缓存可以帮助解决某些构建问题。
- 使用场景:遇到奇怪的构建错误且怀疑可能是由于缓存引起的问题时。
podfile.lock文件介绍
Podfile.lock 是 CocoaPods 自动生成的一个 依赖锁定文件,它的作用是精确记录当前项目中所有依赖库的实际版本号和依赖关系树。这个文件对于团队协作、版本控制以及确保构建一致性非常重要。
📄 一、Podfile.lock 的作用
✅ 1. 固定依赖版本
Podfile中的版本号(如'~> 3.0','>= 2.5')是一个范围限制,不是具体版本。Podfile.lock则会记录实际安装的每个 Pod 的确切版本,例如AFNetworking (3.2.1)。- 这样可以保证在不同机器上执行
pod install时得到一致的结果,避免因为自动更新到新版本而导致的问题。
✅ 2. 记录完整的依赖树
- 每个你显式引入的 Pod 可能还会依赖其他 Pod(称为“子依赖”或“传递依赖”)。
Podfile.lock会完整记录这些依赖之间的关系,比如:
AFNetworking
-> PodsDummy_Pods_MyApp
-> MBProgressHUD
-> NSPort
✅ 3. 支持可重复构建
- 在 CI/CD 环境中(如 Jenkins、GitHub Actions),你可以通过
Podfile.lock确保每次构建使用的是相同的依赖版本。 - 如果没有这个文件,每次运行
pod install都可能拉取最新版本,可能导致不稳定的构建结果。
什么时候更新?
当你修改了 Podfile(添加、删除、修改版本)并运行 pod install 或者 pod update
CocoaPods 会重新解析依赖,并更新 Podfile.lock。
⚠️ 2. 是否应该提交到 Git?
✅ 必须提交!
- 它是团队协作的关键文件之一。
- 不提交
Podfile.lock会导致不同开发者安装不同版本的 Pods,容易引发兼容性问题。
❌ 3. 不要手动编辑它
- 它由 CocoaPods 自动生成和维护。
- 手动修改可能会导致解析失败或依赖冲突。
CocoaPods学习
CocoaPods源码分析
| CocoaPods-Core | CocoaPods 的核心库,负责处理 Podspec 文件和依赖管理的核心逻辑。用途: 解析和处理 Podspec、执行依赖解析、生成目标配置等。 |
|---|---|
| CocoaPods-Downloader | 功能: 用于下载 Pods 的库,支持从 Git、HTTP、Mercurial 等不同来源下载代码。用途: 管理 Pods 的下载过程,确保依赖包可以被正确获取和缓存。 |
| Molinillo | 功能: 通用的依赖解析器,用于解决依赖关系中的版本冲突。用途: 在 CocoaPods 中用于解析 Pods 的依赖关系,确保各依赖项之间的版本兼容性。 |
| Xcodeproj | 功能: 处理 Xcode 项目文件 (.xcodeproj) 的库。用途: 允许 CocoaPods 读取、修改和生成 Xcode 项目文件,从而在项目中集成 Pods。 |
| CocoaPods-Plugin | 功能: CocoaPods 的插件系统,允许开发者扩展 CocoaPods 的功能。用途: 开发者可以通过编写插件,增加或自定义 CocoaPods 的行为和功能。 |
pod install详细流程
| Prepare | 读取 Podfile 和 Podfile.lock,解析项目配置,识别需要安装或更新的依赖项。 |
|---|---|
| Resolve Dependencies | 解析依赖关系,确定各依赖库的具体版本,确保依赖间兼容,不引入不兼容的库版本 |
| Download Dependencies | 下载所需的依赖库(本地缓存或远程服务器),获取必要的源文件 |
| Validate Targets | 验证各目标的配置,确保依赖设置与项目平台、编译环境等兼容,不符合的会发出警告。 |
| Integrate | 将依赖库集成到 Xcode 项目,生成 Pods 项目文件,更新 .xcconfig 和 xcworkspace |
| Write Locks | 将解析到的依赖版本写入 Podfile.lock,记录当前安装版本,确保后续安装一致性。 |
| Post Install | 执行 post_install 钩子(如定义在 Podfile 中),允许用户在安装后执行自定义脚本 |
pod install ,pod install --repo-update, pod update的区别
pod install: 如果 Podfile.lock 存在,pod install 会根据 Podfile.lock 中的版本来安装依赖,以确保与上次安装时的版本一致,确保依赖的一致性。如果 Podfile.lock 不存在,它会根据 Podfile 文件中的依赖进行安装,并生成 Podfile.lock 文件来记录此次安装的依赖版本。
pod install --repo-update: 在执行 pod install 时,添加 --repo-update 选项会首先强制更新本地的 CocoaPods 规格仓库(spec repo)。更新完成后,它会重新解析依赖关系并安装依赖项。这可能会导致依赖版本发生变化,即使 Podfile.lock 已经存在。
pod update: pod update 会忽略 Podfile.lock 中的现有版本约束,根据 Podfile 中的要求更新指定的依赖项到最新版本,并更新 Podfile.lock 文件以记录新的版本。这样确保了所有依赖项都是最新的,且 Podfile.lock 中的内容也相应更新。
CocoaPods的 hook 函数
| hook函数名称 | 简介 | 用途 |
|---|---|---|
| pre_install | 在Pod安装开始之前 | 修改Podfile或执行准备工作 |
| pre_integrate | 在Pods集成到Xcode项目之前 | 自定义Pods项目设置或执行额外操作 |
| post_install | 在Pod安装完成之后 | 调整Xcode项目设置或进行清理 |
| post_integrate | 在Pods集成到Xcode项目之后 | 最终调整 Xcode 项目或执行其他集成后的操作 |
| source_provider | 在处理源代码库时 | 定义或修改 Pod 源代码库的提供方式 |
如何实现自定义hook
当CocoaPods提供的hook点无法满足我们的诉求的时候,我们可以通过以下代码来实现自定义hook,下述hook可以实现hook到CocoaPods的任意方法
代码是通过 Ruby 的 Method Swizzling(方法替换) 技术,动态替换 CocoaPods 内部方法,以达到 Hook 效果。这种技术可以让你 hook 到任意方法,比如下载 Pod 的耗时统计。
- 使用
instance_method获取原始方法; - 使用
define_method替换原方法; - 在新方法中调用旧方法,并加入自定义逻辑(如计时);
# lucifer_hook.rb
module Pod
class Installer
# 保存原始方法
old_download_source = instance_method(:download_source_of_pod)
# 替换方法
define_method(:download_source_of_pod) do |pod_name|
start_time = Time.now
puts "\e[33m📥 开始下载 #{pod_name}...\e[0m"
# 调用原始方法
old_download_source.bind(self).(pod_name)
end_time = Time.now
duration = (end_time - start_time).round(2)
puts "\e[32m✅ #{pod_name} 下载完成,耗时 #{duration} 秒\e[0m"
end
end
end
示例代码
### Hook 下载 Pod 的耗时统计```
# Podfile
# 引入自定义 Hook 脚本
load 'lucifer_hook.rb'
platform :ios, '13.0'
use_frameworks!
target 'MyApp' do
pod 'AFNetworking', '~> 4.0'
end
# --- Hook 示例 ---
# pre_install: 安装前打印提示信息
pre_install do |installer|
puts "⏳ 正在准备安装依赖..."
end
# pre_integrate: 集成前修改 Pods 项目设置
pre_integrate do |installer|
puts "🛠️ 正在预处理 Pods 项目..."
end
# post_install: 安装完成后调整 Xcode 设置
post_install do |installer|
puts "✅ 安装完成,正在调整 Xcode 设置..."
installer.pods_project.targets.each do |target|
if target.name == 'AFNetworking'
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '5.7'
end
end
end
end
# post_integrate: 集成完成后输出日志
post_integrate do |installer|
puts "🎉 Pods 已成功集成到项目!"
end
# source_provider: 自定义源提供方式(可选)
source_provider do |sources|
sources.unshift('https://github.com/yourname/your-private-specs.git')
end