目录
1. Cocoapods工作原理
2. CocoaPods安装和更新问题
3. pod install 和pod update 区别
4. Podfile.lock和Manifest.lock
5. podspec 什么鬼
6. pod常用命令
7. CocoaPods使用中常遇问题
- CocoaPods已经导入第三方库,但头文件却not found
- 使用
CocoaPods
组织管理后,项目会生成ProjectName.xcworkspace、Podfile.lock、Pods
等文件,我们通过Podfile引入的第三方库主要由Pods工程中的Pods-ProjectName-frameworks.sh脚本
负责
在每次编译的时候该脚本帮我们把预引入的所有三方库文件打包的成ProjectName.a静态库文件,放在我们原Xcode工程中Framework文件夹下,供工程使用
Resource文件:Resource资源文件主要由Pods工程中的Pods-ProjectName-resources.sh脚本负责,在每次编译的时候,该脚本会帮你将所有三方库的Resource文件copy到目标目录中。
依赖参数设置:在Pods工程中的的每个库文件都有一个相应的SDKName.xcconfig,在编译时,CocoaPods就是通过这些文件来设置所有的依赖参数的,编译后,在主工程的Pods文件夹下会生成两个配置文件,Pods-ProjectName.debug.xcconfig、Pods-ProjectName.release.xcconfig。
简单理解
CocoaPods的原理是将所有的依赖库都放到另一个名为Pods的项目中,然后让主项目依赖Pods项目,这样,源码管理工作都从主项目移到了Pods项目中。Pods项目最终会编译成一个名为libPods.a的文件,主项目只需要依赖这个.a文件即可。
-
Cocoapods 安装问题集合
在项目开发中CocoaPods初次安装或者更新时,总会遇到各种问题,但是一直不清楚这些问题导致的原因,喜欢刨根问底的我们怎么能够容忍,所以还是要知道Why。
1、先通过命令$ gem sources -l
查看当前你的ruby源
输出下面的 Ruby 默认源需要移除更换:
*** CURRENT SOURCES ***
https://rubygems.org/
说明:
ruby源默认是:https://rubygems.org/ 这个源路径国内不能使用
所以一般使用淘宝的镜像https://ruby.taobao.org/
- 删除ruby默认源、添加国内支持的源
gem sources --remove https://rubygems.org/
gem sources -a https://ruby.taobao.org/
在执行gem sources -a https://ruby.taobao.org/
时可能会遇到下面的问题
ERROR: SSL verification error at depth 0: certificate has expired (10)
ERROR: Certificate /C=CN/ST=ZheJiang/L=HangZhou/O=Alibaba (China) Technology Co., Ltd./CN=*.taobao.org expired at 2018-12-05T03:16:02Z
Error fetching https://ruby.taobao.org/:
SSL_connect returned=1 errno=0 state=error: certificate verify failed (https://ruby.taobao.org/specs.4.8.gz)
查看资料导致该问题是因为taobao Gems 源已停止维护,现由 ruby-china 提供镜像服务,
即我们要用镜像源:http://gems.ruby-china.org
即执行 gem sources -a http://gems.ruby-china.org
,执行该命令可能会出现下面Error
Error fetching http://gems.ruby-china.org:
bad response Not Found 404 (https://gems.ruby-china.org/specs.4.8.gz)
因为该镜像源地址可能会时常发生变化,所以遇到 Error ...404页面没找到说明源已经变了,此时就点击 http://gems.ruby-china.org 该地址进去看下当前的源变成了什么
源果然变化了,所以我们添加变化后的新源即可
即执行 gem sources -a https://gems.ruby-china.com
,执行成功之后gem source -l
查看一下
*** CURRENT SOURCES ***
https://gems.ruby-china.com
此时我们已经替换成新的源了
- 执行
sudo gem install cocoapods
命令安装cocoapods
-
cocoapods
安装成功之后执行pod setup
命令
为什么要执行pod setup ??
因为所有的pod项目的 podspec文件都托管在 https://github.com/CocoaPods/Specs 这个远程索引库
第一次执行pod setup时CocoaPods会将这些podspec索引文件更新到本地的~/.cocoapods/目录下
这个索引文件比较大有 300多M左右,所以第一次更新时非常慢
如果对于索引库、podspec文件不明白的朋友可以查看这篇文章
怎样查看pod setup执行进度、解决pod setup 的慢看这篇文章
更新问题
Xcode10版本工程cocoapod <=1.5.3问题
RuntimeError - [!] Xcodeproj doesn't know about the following attributes {"inputFileListPaths"=>[], "outputFileListPaths"=>[]} for the 'PBXShellScriptBuildPhase' isa.
CocoaPods with Xcode 10 RuntimeError
解决办法:通过 sudo gem install cocoapods 重新安装也是升级cocoapods
此时一般也会遇到上面说到的问题,相应解决就ok了
-
pod install & pod update
何时使用pod install以及何时使用pod update?
pod install :
- 第一次在工程里面使用pods的时候使用,并且每次编辑你的Podfile(添加新库、移除库、更新库)的时候使用
- 每次运行pod install命令的时候会把你安装或者更新的每个库的版本都记录在Podfile.lock文件里面
- Podfile.lock文件记录你每个安装库的版本号,并且锁定了这些版本。
当你使用pod install会去查看在Podfile.lock里面所列出的那些库,然后安装在Podfile.lock里面明确的版本。不会去检查该库是否有新的版本。对于还不在Podfile.lock中的库,会找到Podfile里面描述对应版本(例如:pod "MyPod", "~>1.2")。
pod update:
当你运行 pod update PODNAME
命令CocoaPods会帮你更新这个库最新版本,而不需要考虑Podfile.lock里面的限制。如果你运行pod update,后面没有跟库的名字,CocoaPods就会更新每一个Podfile里面的库到最新版本。
总结:
开发中当你在Podfile里面添加了一个Pod库的时候,你应该使用pod install,这个命令不会更新那些已经安装了的库;要更新某个库的时候你应该使用pod update PODNAME去更新某个特定的库,而不是pod update(pod update会更新所有的库)。
举例:以下会举例说明在各个场景下的使用。
场景1:User1创建了一个工程
User1创建了一个工程,并且想使用A、B、C这三个库,所以他就创建了一个含有这个三个库的Podfile,并且运行了pod intall。这样就会安装了A、B、C三个库到这个工程里面,假设我们的版本都为1.0.0。因此Podfile.lock跟踪并记录A、B、C这三个库以及版本号1.0.0。
顺便说一下:由于这个工程是第一次运行pod install,并且Pods.xcodeproj工程文件还不存在,所以这个命令也会同时创建Pods.xcodeproj以及.xcworkspace工程文件,这只是这个命令的一个副作用,并不是主要目的。
场景2:User1添加了一个库之后
User1添加了一个库D到Podfile文件中。然后他就应该运行pod install命令了。所以即使库B的开发者发布了B的一个新版本1.1.0。但只要是在第一次执行pod install之后发布的,那么B的版本仍然是1.0.0。因为User1只是希望添加一个新库D,不希望更新库B。
这就是很多人容易出错的地方,因为他们在这里使用了pod update,因为想着“更新我的工程一个新的库而已”。这里要注意!
场景3:User2加入到这个工程中
然后,User2,一个之前没有参与到这个工程的人,加入了。他clone了一份仓库,然后使用pod install命令。Podfile.lock的内容就会保证User1和User2会得到完全一样的pods,前提是Podfile.lock被提交到git仓库中。
即使库C的版本已经更新到了1.2.0,User2仍然会使用C的1.0.0版本,因为C已经在Podfile.lock里面注册过了,C的1.0.0版本已经被Podfile.lock锁住了。
场景4:检查某个库的新版本
之后,User1想检查pods里面是否有可用的更新时,他执行了pod outdated,这个命令执行后,会列出来:B有了1.1.0版本,C有了1.2.0版本。这时候,User1打算更新库B,但不更新库C,所以执行pod update B,这样就把B从1.0.0更新到1.1.0(同时更新Podfile.lock里面对B的版本记录),此时,C仍然是1.0.0版本,不会更新。
在Podfile中使用明确版本还不够
有些人认为在Podfile中明确某个库的版本
例如:pod 'A', '1.0.0' ,足以保证所有项目里面的人都会使用完全一样的版本。这个时候,他们可能会觉得,此时如果添加一个新库的时候,我使用pod update并不会去更新其它的库,因为其它的库已经被限定了固定的版本号。
但事实上,这不足以保证User1和User2的pods中库的版本会完全一样。一个典型的例子是,如果库A有一个对库A2的依赖(声明在A.podspec中:dependency 'A2', '~> 3.0'),如果这样的话,使用 pod 'A', '1.0.0' 在你的Podfile中,的确会让User1和User2都使用同样版本的库A(1.0.0).
然而:最后User1可能使用A的依赖库A2的版本为3.4(因为3.4是当时User1使用的最新版本),但User2使用的库A2版本是3.5(假设A2的开发者刚刚发布了A2的新版本3.5)。
所以只有一个方法来保证某项目的每个开发者都使用相同版本的库,就是每个电脑中都使用同样的Podfile.lock,并且合理使用pod install 和 pod update。
-
Podfile.lock & Manifest.lock是什么
当我们项目里集成了CocoaPods并第一次执行完pod install
后
- 在项目根目录生成一个Podfile.lock文件 在Pods目录下生成Manifest.lock文件
- Podfile.lock 中会指明项目当前依赖库的版本 (包括Podfile 中直接写明使用的库以及这些库依赖的其他库)
当你跟小伙伴协同开发时,小伙伴同步了你的 Podfile.lock 文件后,他执行 pod install
会安装 Podfile.lock 指定版本的依赖库,这样就可以防止大家的依赖库不一致而造成问题。因此,CocoaPods 官方强烈推荐把 Podfile.lock 纳入版本控制之下。
注意:Podfile.lock 并不是一成不变的
当你修改了 Podfile 文件里使用的依赖库或者运行pod update
命令时,就会生成新的 Podfile.lock 文件。
Podfile.lock 主要包含以下几部分
PODS:
- YYModel (1.0.4)
DEPENDENCIES:
- YYModel
SPEC CHECKSUMS:
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
PODFILE CHECKSUM: 65f13d3af89ad0d2828bfc5b5919652fb43376fc
COCOAPODS: 1.4.0
协同开发时注意使用 pod install
和 pod update
的区别:
使用pod install
只会安装 Podfile 中新改变的东西
并且会:优先遵循 Podfile 里指定的版本信息
其次遵循 Podfile.lock 里指定的版本信息来安装对应的依赖库
比如:
在Podfile里没指定AFN的版本,但Podfile.lock 里指定了AFN 版本是 3.0.1,那么即使现在有最新的 3.0.4,最终也会安装3.0.1
但如果Podfile里指定了AFN版本是3.0.3则会安装 3.0.3,并更新Podfile.lock里的版本信息
使用pod update
根据Podfile的规则更新所有依赖库
不会去看Podfile.lock,而是根据安装依赖库的情况生成新的 Podfile.lock文件
Manifest.lock 又是啥?
- Manifest.lock 相当于 Podfile.lock 的副本
- 执行
pod install
后在Pods目录下生成Manifest.lock文件 - Build时会执行 Build Phases 中的[CP] Check Pods Manifest.lock 脚本检查一下 Podfile.lock 和 Manifest.lock 是否一致,如果不一致就会报错
问:既然有了Podfile.lock那为啥还要有Manifest.lock?
这样做的原因是 Pods 目录并不总是被放到版本控制之下,有了这个检查机制就能保证开发团队的各个小伙伴能在运行项目前更新他们的依赖库,并保持这些依赖库的版本一致,从而防止在依赖库的版本不统一造成程序在一些不明显的地方编译失败或运行崩溃。
问:Podfile.lock报版本找不到问题或版本不一致问题?
场景1:
协同开发时有天一个小伙伴升级了Podfile中某个库的版本并提交到远程(比如AFN从3.0.1升级到了3.0.4),然后你更新了之后执行了pod install... 然后爆红了
场景2:
团队小伙伴开了一个私有库A,我们项目里只是指定了 pod A但是没有指明版本,此时A小伙伴将A库的进行了优化更新了版本1.1.1并提及到了远程代码仓库;当我们更新并执行pod install时会先查看Podfile.lock 里指定的A库的版本,然后CocoaPods会查看Podfile.lock 里指定的A库的版本是否和我们本地索引库里A库的版本是否一致,不一致然后爆红
场景...
当你更新代码执行pod install爆红
可能是你本地的Podfile.lock里库版本 和远程服务器上的Podfile.lock的库版本不一致时;
可能是你本地索引库里库的版本和Podfile.lock里指定的库的版本不一致
根据具体情况可以参考一下解决方法
- 删掉.lock文件再pod install一次,如果仍然报错
- 执行 pod repo update 更新本地索引库 就是本地CocoaPods的spec资源配置信息;如果不知道本地索引库是什么可以参看这篇文章里面说的很清楚 ,然后在执行pod install
比如下面的报错问题:直接 pod repo update之后在pod install 完美解决
怎么对待 Podfile.lock
以下建议可供参考:
- Podfile.lock 应该加入版本控制,保证小伙伴们的依赖库版本一致,防止出现难以发现的 bug
- 在初始创建项目的时候就建议加入版本控制
- Podfile 中最好指定明确的依赖库版本
- 明白 pod install 和 pod update 的区别以及它们对 Podfile.lock 的影响,合理使用
pod命令
创建默认的 Podfile
$ pod init
第一次使用安装pod库
$ pod install
安装pod库,不更新本地索引,速度快,但是不会升级本地代码库
$ pod install --no-repo-update
升级所有pod库
$ pod update
更新pod库,不更新本地索引,速度快
可以安装新pod库或者删除不用的pod库,但是不会升级项目已经安装的pod库
$ pod update --no-repo-update
查看哪些pod库有更新版本,如果习惯使用 --no-repo-update
参数,这个命令就显得格外重要了
$ pod outdated
搜索pod库
- 空格 下一页
- q 退出
- / 搜索
$ pod search AFNetworking
只搜索复合名字的pod库,这个对于搜索结果非常多时,尤其有用
$ pod search AFNetworking --simple
帮助
$ pod --help
Podspec 文件
关于Podspec文件详细说明,可以参看这篇文章 - Podspec语法参考 v1.2.0.beta.1
-
Cocoapods使用中常见问题
Q1: Cocoapods导入第三方库,但头文件却显示没有发现
方案一
Product -> Clear Build Folder (按住option) 清理一下,重新Build一次
方案二
选择Target -> Build Settings 菜单,找到\”User Header Search Paths\”设置项
新增一个值"${SRCROOT}",并且选择\”Recursive\”
方案三 看Q2中的方式
Q2: 如果我们的开源库依赖系统库怎么办?
# s.framework = 'AAAFramework'// 去掉#,设置依赖的系统库名称
# s.frameworks = 'AAAFramework', 'BBBFramework'//设置多个系统库名称
# s.library = 'iconv'// 设置只依赖一个系统的library
# s.libraries = 'iconv', 'xml2' // 设置依赖多个系统的library
# s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }// 这里是工程配置,这样使用者就不需要手动处理,由pod自动处理了,也是Q1中问题的一种解决方法。
比如我们自己的开源库依赖 SystemConfiguration.framework
,AVFoundation.framework
,CoreTelephony.framework
和libc++.tbd
,libxml2.tbd
,libsqlite3.0.tbd
这几个系统库,就可以在podspec文件中指定如下:
s.frameworks = 'SystemConfiguration', 'AVFoundation', 'CoreTelephony'
s.libraries = 'c++', 'sqlite3.0', 'xml2'
s.xcconfig = { 'HEADER_SEARCH_PATHS' => '$(SDKROOT)/usr/include/libxml2' }
Q3: 如果我们的开源库依赖其他第三方静态库?
s.vendored_frameworks = 'Framewroks/*.framework' // 注意这里指定的路径
s.vendored_libraries = 'Framewroks/*.a'
因为.framework和.a静态库都存放在Framewroks这个文件夹下,该文件夹和.podspec文件在同级目录,所以上面的路径指定就是 Framewroks/*.framework
,实际路径需要根据你存放静态库的路径来指定
Q4: Bundle资源的依赖
s.resource_bundles = {
'AAASDK' => ['Framewroks/Resources/AAASDK.bundle'],
'BBBSDKLib' => ['Framewroks/Resources/BBBSDK.bundle']
}
Q5: 关于Pod子库的制作可以参看这篇文章subspec子库的制作
参看文档
pod install vs. pod update
使用 pod install 还是 pod update ?
关于 Podfile.lock 带来的痛-这篇文章讲述的很
Podfile语法参考-官方指南
# Cocoapods的使用教程