目的:构建自己的一个组件,放在公司的私有pod库中,然后可以通过cocoapod来导入这个pod库。
一、几个概念
在构建私有库之前,先了解以下几个基本概念
- repo(repository仓库)
- Pod(代码库)。分为:(Pod译为(宇宙飞船的)分离舱,Pod对于主项目,如iPod之于Mac,AirPods之于iPhone)
- 公有库:放在外网上,其他开发者是可以访问使用的
- 私有库:放在本地或者内网上,只有公司内部的人员可以使用
- podspec文件(spec说明书)。创建pod库的时候会用到podspec文件将lib的一些信息,比如:版本号、作者名,链接地址等,以及其中用到的包括资源、源码、需要引用的framework,以及第三库等等组织起来。
- Spec Repo(存放spec的仓库)。顾名思义,就是存放Spec文件的仓库,就是一个容器,所有公开的Pods都在这个里面,是一个Git仓库remote端。
- 执行 pod setup命令会clone该仓库到本地的~/.cocoapods/repos目录下,可以进入到这个目录看到master文件夹就是这个官方的Spec Repo了。
- Spec Repo仓库保存了依赖库的名称,版本号,以及spec文件。
- 因为Spec Repo存放了所有的spec文件,所以很明显它是一种集中式的依赖库管理工具。
创建公有Pod库或者私有Pod库,实际上原理是一样的,都是基于git服务和repo协议。
不一样的是,两者的版本索引查询方式不一样,公有库的podspec由CocoaPods/Specs管理,而内部私有使用的pod库需要自己建立一个仓库(Spec Repo)来管理podspec。
好了,下面我们正式来构建私有的pod库
二、构建私有pod库
2.1 创建组件仓库
在公司的私有Git服务器上创建git仓库,用来管理我们的组件模块,将仓库Clone
下来,把模块代码Push
上去。
2.2 创建组件的.podspec文件
私有仓库的根目录下,执行下面命令,创建一个新的.podspec
文件,在这个文件中来进行私有仓库的一些配置工作。
1 | pod spec create TestModule # 生成TestModule.podspec |
2.2.1 编写.podspec(源码或framework)
podspec 文件里面的东西,描述了我们代码库的一些特征,例如name
、source
、version
之类的,在进行pod search
操作的时候也会显示。
1 | Pod::Spec.new do |s| |
以上 source
字段表示导入的是源码形式的依赖。也可以是 framework
形式。两种方式,各有利弊。
- 直接在主工程中集成代码文件,可以看到其内部实现源码,方便在主工程中进行调试。
- 集成
framework
的方式,可以加快编译速度,而且对每个组件的代码有很好的保密性。如果公司对代码安全比较看重,可以考虑framework
的形式。
以友盟的SDK为例,UMCCommon.podspec.json
:
1 | { |
2.2.2 使用subspec目录分层
目录分层的好处:
- 目录分层,结构清晰;
- 使用pod引入一个三方库时,可以只引入一个subspec而不用将整个三方库引入。
例如AFNetworking:
下面举例说明,如图所示的目录结构:
1 | ZCPKit |
写法如下:
效果:
如果想有多层的目录结构还可以继续嵌套下去。
有几个需要注意的地方:
- 层级不能出现循环依赖。
- 比如类ZCPUtil.h中 #import “ZCPRouter.h”,同时ZCPRouter.h中 #import “ZCPUtil.h”。这样当写podspec时就需要在Util层级中写dependency ‘ZCPKit/Router’,在Router层级中写dependency ‘ZCPKit/Util’。如此便存在Router与Util层级之间的循环依赖。出现循环依赖时,三方库是无法成功提交到repo上的,会报依赖错误。解决办法是,层级间要尽量解耦。
- 分层的层级不要太多,层级不要太深。为了避免出现上述的循环依赖错误。
- source_files使用的是真实的物理路径,而dependency依赖其他层级时使用的是层级路径,不是真实的物理路径。
- 例如:ZCPUtil.h文件的真实路径是:ZCPKit/Framework/Util,而Util层级是属于ZCPKit层级下的一个子subspec,所以当写Router层级依赖Util层级时要写:dependency ‘ZCPKit/Util’而不是dependency ‘ZCPKit/Framework/Util’
2.2.3 校验
改完,可以用pod
命令验证一下这个.podspec
文件有没有问题。
1 | $ pod spec lint # pod spec: Manage pod specs. + lint: Validates a spec file |
2.3 创建Spec Repo
在公司的私有Git服务器上创建git仓库,用来做内部私有库的Spec Repo,盛放所有功能组件的spec。
这个Spec Repo必须添加到本地,否则组件clone不下来
1 | # 执行这个命令的人,必须得有这个库的操作权限 |
添加完成后,用下面命令检查一下:
1 | pod repo lint # pod repo: Manage spec-repositories. lint: Validates all specs in a repo |
2.4 将podspec同步到Spec Repo
之后,当我们修改了Pod库源代码后,我们也要更新.podspec文件,并同步到spec 仓库。
- 修改podspec中的version
- 提交代码,打Tag,注意:这个tag记得要提交到origin,默认是只在工作副本中打Tag,”tag = podspec中的version“
pod repo push <Specs库名> <Pod库名>.podspec
将podspec推送到 spec Repo中
2.5 使用私有Pod库代码
当Spec
私有仓库中有可用的代码后,就可以通过CocoaPods
命令来使用组件代码了。在Podfile
文件中需要声明私有sepc repo地址,例如下面代码。
1 | source 'https://github.com/CocoaPods/Specs.git' # CocoaPods官方的sepc repo |
版本控制也非常简单,只需要在Podfile
中指定某个私有仓库的版本号即可。
三、遇到的问题
The NAME.podspec specification does not validate
用pod spec lint –verbose 验证一下,会打印出很多的详细信息,此时打印:The spec did not pass validation, due to 1 warning (but you can use
--allow-warnings
to ignore it).此时,可重新使用pod repo push Specs xx.podspec –allow-warnings。
更新某个库时,连接超时。fatal: unable to access '...url...': Failed to connect to ...host... port 443: Operation timed out
。解决步骤:
pod repo
列出所有(公有库、私有库的)本地库,查看 cocoapods的本地库路径,一般都是.cocoapods/repos/master
- 从报错信息中,可以看到操作失败的三方库的名称、版本号,使用shell命令
find
进行查找find ~/.cocoapods/repos/master -iname 名称
,会返回在本地库中的路径。前往该路径 - 三方库文件夹下会有多个版本文件夹,找到报错信息中的版本号对应的文件夹,里面会有一个
xxx.podspec.json
,替换json中source下git地址(可以直接Google,也可以在GitHub上搜索该库名称,看项目介绍,是否是这个库的镜像)。
四、Local Pods
私有仓库调试比较麻烦,不可能是每修改一些代码,就提交到远端仓库,再拉下来验证代码是否有效。所以,为了提升开发效率,可以通过LocalPods
的方式进行调试。或者不想用远端仓库,也可以直接用LocalPods
的方案进行本地组件化,但是并不推荐这种方案,因为不能进行物理隔离。
本地LocalPods
仓库不需要单独创建,直接用之前的组件仓库。在pods
中使用本地仓库时,需要指定对应路径。指定路径后,执行pod install
即可集成本地仓库。为了方便指定本地路径,建议将组件仓库尽量放在一个文件夹下,例如叫做LocalPods
。
1 | pod 'Login', :path => './LocalPods/Login/' |
通过上面的命令集成本地仓库后,Pods
会生成一个和远程仓库文件夹同级的文件,叫做Development Pods
,这个文件夹就是本地仓库所在的位置。
此时之前Login
仓库的产物就被替换为本地 LocalPods/Login 代码,后面的修改都会影响这个产物。直到调试完成,将代码提交到组件仓库,将path
路径删除重新执行pod install
,产物就会被替换为我们的私有Pod库代码。