2018-07-05 使用Cocoapods创建并管理私有仓库
使用Cocoapods创建并管理私有仓库
Cocoapods 是一个非常好用的iOS依赖管理工具,本文主要介绍在Git环境下如何用他来创建我们自己的私有仓库。
依照惯例,先讲一下Cocoapods的优点 O(∩_∩)O:
- 可以方便的管理和更新项目中所使用到的第三方库;
- 可以将自己的项目中的公共组件交由它去管理;
- 可以设置自己的私有库(本文)。
创建私有仓库的主要步骤:
- 创建一个私有的 Spec Repo。(从未创建过私有库时需要,只需创建一次)
- 创建 具体的Pod 需要的项目工程文件。(2和3 二选一)
- 创建 具体的Pod 对应的 .podspec 文件。(2和3 二选一)
- 本地测试 .podspec 文件及 Pod库 功能。
- 向私有的 Spec Repo 中提交 .podspec。
- 使用创建好的 Pod 库。
- 更新维护podspec。
1.创建私有 Spec Repo
首先介绍一下 Spec Repo,它是所有的 Pods 的一个索引,实际也是一个Git仓库,远端在GitHub、其他代码托管平台或自家的服务器上,但是当你使用了Cocoapods后会被clone到本地的 ~/.cocoapods/repos 目录下,进入此目录后看到的master文件夹就是官方的Spec Repo,本文中自己创建的私有库会在和master平级的位置。master目录的结构大概是这样的:
.
├── Specs
└── [SPEC_NAME]
└── [VERSION]
└── [SPEC_NAME].podspec
因此我们需要创建一个类似于 master 的私有 Spec Repo,这里我们自己创建一个 Git仓库,这个仓库你可以创建私有的也可以创建公开的(此处我创建了公开的),对于私有仓库,如果项目中有其他同事共同开发的话,你还要给他这个Git仓库的权限。这里我使用了码云。
在服务端创建,创建完成之后在Terminal中执行如下命令:
# pod repo add [Private Repo Name] [remote repository clone URL]
$ pod repo add riversea2015_mobile_iphone_repo_local https://gitee.com/riversea2015/riversea2015_mobile_iphone_repo.git
说明1:riversea2015_mobile_iphone_repo_local 是本地显示的名字,可以和后边链接里的 riversea2015_mobile_iphone_repo 名称不同(⊙o⊙)哦!
说明2:如果有其他合作人员共同使用这个私有Spec Repo的话在他有对应Git仓库的权限的前提下执行相同的命令添加这个 Spec Repo 即可。
至此第一步创建私有 Spec Repo 完成。
* 如果有现有的组件项目,并且在Git的版本管理下,那么可以直接进行第3步了。
* 如果你的组件还在你冗余庞大的项目中,需要拆分出来或者需要自己从零开始创建一个组件库,建议使用Cocoapods提供的一个工具创建工程文件,详见第2步。
2.创建Pod项目工程文件
2.1 创建项目
此处使用Cocoapods提供的一个工具创建单独的pod项目文件,工具的文档介绍是 Using Pod Lib Create, 就拿我创建的 HHPodTestLibrary 为例介绍一下具体操作:
# 先cd到要创建项目的目录然后执行
$ cd /Users/sea/Documents
# 然后开始创建
$ pod lib create HHPodTestLibrary
之后他会问你5个问题(具体介绍见官方文档,箭头后边是我的选择):
- 0.选择编程语言swift/objc?--> objc
- 1.是否需要一个例子工程?--> Yes
- 2.选择一个测试框架?--> Specta/Expecta
- 3.是否基于View测试?--> Yes
- 4.类的前缀?--> HH
问完以上5个问题,会自动执行 pod install 命令创建项目并生成依赖,然后会默认用Xcode将工程打开。
2.2 添加库文件
这里 我在对应的 Classes 中添加了4个文件,然后进入Example文件夹执行 pod update 命令,再打开项目工程可以看到,刚刚添加的组件已经在 Pods 子工程下的 Development Pods/HHPodTestLibrary中了。
* 这里需要注意的是每当你向Pod中添加了新的文件或者以后更新了 podspec 的版本都需要重新执行一遍 pod update 命令。
测试无误后需要将该项目添加并推送到远端仓库,并编辑podspec文件。
具体操作如下:
通过Cocoapods创建出来的目录本身就在本地的Git管理下,我们需要做的就是给它添加远端仓库,同样去GitHub或其他的Git服务提供商那里创建一个私有的仓库,拿到SSH或HTTPS地址,然后cd到PodTestLibrary目录
提交修改:
$ git add .
$ git commit -s -m "Initial Commit of Library"
$ git remote add origin https://gitee.com/riversea2015/HHPodTestLibrary.git #添加远端仓库(SSH或Https)
$ git push origin master #提交到远端仓库
* 注意,这里是将整个目录(pod和Example) 上传到下边创建的私有仓库,这样后期直接拉下来更改,就顺便在Example里边测试功能。
2.3 配置/修改 .podspec 文件
现在开始编辑podspec文件了,它是一个 Ruby 的文件,把编辑器的格式改成 Ruby 就能看到语法高亮,下面我贴上我的 podspec 文件,并在后面以注释的形式说明每个字段的含义,没有涉及到的字段可以去官方文档查阅。(此处我的文件没有删除注释,实际操作时可以删掉注释)
#
# Be sure to run `pod lib lint HHPodTestLibrary.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'HHPodTestLibrary' # 名称
s.version = '0.1.0' # 版本号
s.summary = 'This is the first pod repo test library.' #简介
s.description = <<-DESC
This is the first pod repo test library! #详细介绍
DESC
s.homepage = 'https://gitee.com/riversea2015/HHPodTestLibrary' #主页,尽量填写可以访问到的地址,否则验证不通过
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' #屏幕截图
s.license = { :type => 'MIT', :file => 'LICENSE' } #主页,这里要填写可以访问到的地址,不然验证不通过
s.author = { 'hehai' => 'hehai682@126.com' } #作者信息
s.source = { :git => 'https://gitee.com/riversea2015/HHPodTestLibrary.git', :tag => s.version.to_s } #项目地址,这里不支持ssh的地址,验证不通过,只支持HTTP和HTTPS,最好使用HTTPS
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>' #多媒体介绍地址
s.ios.deployment_target = '8.0' # 支持的平台版本,此处是iOS8.0以上
s.requires_arc = true #是否使用ARC,如果指定具体文件,则具体的问题使用ARC
s.source_files = 'HHPodTestLibrary/Classes/**/*.{h,m}' # 文件路径
# s.resource_bundles = {
# 'HHPodTestLibrary' => ['HHPodTestLibrary/Assets/*.png'] #资源文件地址
# }
s.public_header_files = 'HHPodTestLibrary/Classes/**/*.h' # 公开的头文件地址
s.prefix_header_contents = '#ifdef __OBJC__','#import "其他pod中的头文件A.h"','#import "其他pod中的头文件B.h"','#import "其他pod中的头文件C.h"','#endif' # 相当于pod中的一个pch文件,可以保证等号右边的头文件会被此podspec文件对应的库中所有文件可见,而其中的#ifdef __OBJC__保证只在OC环境中引入这些头文件,其它如C++等则不会引入。
# s.frameworks = 'UIKit', 'MapKit' #所需的framework,多个用逗号隔开
# s.dependency 'AFNetworking', '~> 2.3' #依赖关系,该项目所依赖的其他库,如果有多个需要填写多个
# s.dependency 'AFNetworking', '~> 2.3'
end
编辑完podspec文件后,需要验证一下这个文件是否可用,如果有任何 WARNING 或者 ERROR 都是不可以的,它就不能被添加到Spec Repo中,不过Xcode的 WARNING 是可以存在的(即,如果你只是本地使用,允许存在个别error和warning),验证需要执行以下命令:
$ pod lib lint
当你看到
-> HHPodTestLibrary (0.1.0)
HHPodTestLibrary passed validation.
就说明验证通过了,不过这只是这个podspec文件是合格的,不一定说明这个Pod是可以用的,后边还需要在本地做一下验证。
提交修改(包括.podSpec文件)
$ git add .
$ git commit -m '编辑podspec文件'
$ git push origin master
* 从此处跳转执行 4 的操作,完成 4 的验证操作后(有可能需要回来修改pod中的文件,并提交),再接着执行下边的打Tag操作。
因为podspec文件中获取Git版本控制的项目还需要tag号,所以我们要打上一个tag,并将tag推送到 remote repository:
$ git tag -m 'first release' '0.1.0'
$ git push --tags
* 至此,可以跳转至 5 执行后边的操作了。
3.创建 具体的Pod 对应的 .podspec 文件。(2和3 二选一)
如果按照 2 进行了操作,请绕行至 4 O(∩_∩)O~
如果已经有了现成的项目,那么就需要给这个项目创建一个podspec文件,创建它需要执行Cocoapods的另外一个命令,官方文档在这里
pod spec create <spec file name> [对应私有仓库的地址,需要实现创建好,否则会失败]
$ pod spec create HHPodTestLibrary https://gitee.com/riversea2015/HHPodTestLibrary.git
执行完之后,就创建了一个podspec文件,他其中会包含很多内容,可以按照我之前介绍的进行编辑,没用的删掉。编辑完成之后使用验证命令验证一下
$ pod lib lint
验证无误就可以进入下一步了。
* 提交代码,并 push 到 remote repository
* 跳转执行 4 的操作,完成 4 的验证操作后(有可能需要回来修改pod中的文件,并提交),再执行打 Tag 并 push tag 的操作。
* 至此,可以跳转至 5 执行后边的操作了。
4.本地测试 .podspec 文件及 Pod库 功能
方案一:可以创建一个新的Demo,在这个项目的 Podfile 文件中直接指定刚才创建编辑好的podspec文件,看是否可用。 在 Podfile 中我们可以这样编辑:
platform :ios, '7.0'
# 以下2种方式均可:
pod 'HHPodTestLibrary', :path => '<指定本地HHPodTestLibrary目录的路径>'
pod 'HHPodTestLibrary', :podspec => '<指定本地 .podspec 文件路径,含文件名>'
方案二:如果我们前边使用的 2 创建项目,就会自动生成Example项目,只需要 cd 到 Example 目录下,执行:
pod install
# 或者
pod update --no-repo-update
在Example中编写代码,测试库功能无误后就可以开始 5 了,提交podspec到Spec Repo中。
* 如果在 Example 的 Podfile中写了 use_frameworks! 则可能导致无法引用pod中的文件,而且pod之间也就互不可见
5.向Spec Repo提交podspec
向Spec Repo提交podspec需要完成两点:
①是podspec必须通过验证无误;
②就是删掉无用的注释(这个不是必须的,为了规范还是删掉吧)。
向我们的私有Spec Repo提交podspec只需要一个命令:
# pod repo push <本地repo的名字> <.podspec 名字>
$ pod repo push riversea2015_mobile_iphone_repo_local HHPodTestLibrary.podspec #前面是本地 Repo 名字 后面是 .podspec 名字
完成之后这个组件库就添加到我们的私有Spec Repo中了,可以进入到~/.cocoapods/repos/riversea2015_mobile_iphone_repo_local目录下查看
.
├── LICENSE
├── HHPodTestLibrary
│ └── 0.1.0
│ └── HHPodTestLibrary.podspec
└── README.md
再去看我们的Spec Repo远端仓库,也有了一次提交,这个podspec也已经被Push上去了。
至此,我们的这个组件库就已经制作添加完成了,使用pod search命令就可以查到我们自己的库了
$ pod search HHPodTestLibrary
-> HHPodTestLibrary (0.1.0)
Just Testing.
pod 'HHPodTestLibrary', '~> 0.1.0'
- Homepage: https://gitee.com/riversea2015/HHPodTestLibrary
- Source: https://gitee.com/riversea2015/HHPodTestLibrary.git
- Versions: 0.1.0 [riversea2015_mobile_iphone_repo repo]
这里说的是添加到私有的Repo,如果要添加到Cocoapods的官方库了,可以使用trunk工具,具体可以查看官方文档。
6.使用制作好的Pod
在完成这一系列步骤之后,我们就可以在正式项目中使用这个私有的Pod了只需要在项目的Podfile里增加以下一行代码即可
$ pod 'HHPodTestLibrary', '~> 0.1.0'
然后执行执行以下命令:
pod update --no-repo-update # 更新库依赖
打开项目可以看到,我们自己的库文件已经出现在Pods子项目中的Pods子目录下了,而不再是Development Pods。
注意:这里 Podfile文件前两行应该是:
source 'https://github.com/CocoaPods/Specs.git' # GitHub的公有仓库
source 'https://gitee.com/riversea2015/riversea2015_mobile_iphone_repo.git' # 码云上的私有仓库
7.更新维护podspec
最后再来说一下制作好的podspec文件后续的更新维护工作,比如如何添加新的版本,如何删除Pod。
现在已经制作好了 HHPodTestLibrary 的 0.1.0 版本,现在对他做一些更新维护工作:
7.1 添加更多的子pod
这次我添加了更多的模块到 PodTestLibrary 之中,包括工具类,底层 Model 及 UIKit 扩展等,这里又尝试了一下 subspec 功能,给 PodTestLibrary 创建了多个子分支。
具体做法是先将源文件添加到Pod/Classes中,然后按照不同的模块对文件目录进行整理,因为我有四个模块,所以在Pod/Classes下有创建了四个子目录,完成之后继续编辑之前的PodTestLibrary.podspec,这次增加了subspec特性
#
# Be sure to run `pod lib lint HHPodTestLibrary.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'HHPodTestLibrary'
s.version = '0.1.3'
s.summary = 'This is the first pod repo test library.'
s.description = <<-DESC
This is the first pod repo test library!
DESC
s.homepage = 'https://gitee.com/riversea2015/HHPodTestLibrary'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'hehai' => 'hehai682@126.com' }
s.source = { :git => 'https://gitee.com/riversea2015/HHPodTestLibrary.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '8.0'
s.requires_arc = true
# s.source_files = 'HHPodTestLibrary/Classes/**/*.{h.m}'
# s.public_header_files = 'HHPodTestLibrary/Classes/**/*.h'
# s.resource_bundles = {
# 'HHPodTestLibrary' => ['HHPodTestLibrary/Assets/*.png']
# }
s.subspec 'First' do |first|
first.source_files = 'HHPodTestLibrary/Classes/First/**/*.{h,m}'
first.public_header_files = 'HHPodTestLibrary/Classes/First/**/*.h'
first.dependency 'AFNetworking', '~> 3.1'
end
s.subspec 'Second' do |second|
second.source_files = 'HHPodTestLibrary/Classes/Second/**/*.{h,m}'
second.public_header_files = 'HHPodTestLibrary/Classes/Second/**/*.h'
second.dependency 'HHPodTestLibrary/First'
end
s.subspec 'Third' do |third|
third.source_files = 'HHPodTestLibrary/Classes/Third/**/*.{h,m}'
third.public_header_files = 'HHPodTestLibrary/Classes/Third/**/*.h'
third.dependency 'FMDB', '~>2.7.1'
end
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
# s.dependency 'OpenUDID', '~> 1.0.0'
end
注意:
* .subspec 'First' do |first| 中 子 pod 的别名必须以小写字母开头,否则 pod lib lint 时会报错,而且并不指明错误原因,会很崩溃的(⊙o⊙)哦
* 默认 pod 与 pod 之间是可以互相引用的,但是 pod 内部是无法直接引用外部文件的,所以需要我们来做这些事情。
因为我们创建了subspec所以项目整体的依赖dependency,源文件source_files,头文件public_header_files,资源文件resource等都移动到了各自的subspec中,每个subspec之间也可以有相互的依赖关系,比如 Second 就依赖于 First。
编辑完成之后,在测试项目里 pod update 一下,几个子项目都被加进项目工程了,写代码验证无误之后,就可以将这个工程push到远端仓库,并打上新的 tag -> 0.1.3。
最后再次使用 pod lib lint 验证编辑好的podsepc文件,没有自身的 WARNING 或者 ERROR 之后,就可以再次提交到 Spec Repo 中了。
$ pod repo push WTSpecs PodTestLibrary.podspec
之后再次到 ~/.cocoapods/repos/riversea2015_mobile_iphone_repo_local 目录下查看
.
├── LICENSE
├── HHPodTestLibrary
│ ├── 0.1.3
│ │ └── HHPodTestLibrary.podspec
│ ├── 0.1.2
│ │ └── HHPodTestLibrary.podspec
│ └── 0.1.0
│ └── HHPodTestLibrary.podspec
└── README.md
3 directories, 4 files
已经有3个版本了,使用pod search查找得到的结果为
-> HHPodTestLibrary (0.1.3)
This is the first pod repo test library.
pod 'HHPodTestLibrary', '~> 0.1.3'
- Homepage: https://gitee.com/riversea2015/HHPodTestLibrary
- Source: https://gitee.com/riversea2015/HHPodTestLibrary.git
- Versions: 0.1.3, 0.1.2, 0.1.0 [riversea2015_mobile_iphone_repo_local repo]
- Subspecs:
- HHPodTestLibrary/First (0.1.3)
- HHPodTestLibrary/Second (0.1.3)
- HHPodTestLibrary/Third (0.1.3)
(END)
完成这些之后,在实际项目中我们就可以选择使用整个组件库或者是组件库的某一子库了,对应的Podfile中添加的内容为
platform :ios, '8.0'
pod 'HHPodTestLibrary/Second', '~>0.1.0' #使用某一个部分(导入了2个子库)
# pod 'HHPodTestLibrary/Third', '~> 0.1.0' #使用某一个部分(导入了1个子库)
# pod 'HHPodTestLibrary', '~> 0.1.0' #使用整个库
注意:此处,因为 Second 依赖于 First,所以当pod update 之后,实际导入项目中的子库总共有2个:Second、Second。
7.2 删除、恢复私有Spec Repo
最后介绍一下如何删除一个私有Spec Repo,只需要执行一条命令即可
$ pod repo remove riversea2015_mobile_iphone_repo_local
这样这个Spec Repo就在本地删除了,我们还可以通过
$ pod repo add riversea2015_mobile_iphone_repo_local https://gitee.com/riversea2015/riversea2015_mobile_iphone_repo.git
再把它给加回来。
7.3 删除私有Spec Repo下的某一个podspec
如果我们要删除私有Spec Repo下的某一个podspec怎么操作呢,此时无需借助Cocoapods,只需要切到riversea2015_mobile_iphone_repo_local目录下,删掉库目录
$ cd ~/.cocoapods/repos/riversea2015_mobile_iphone_repo_local
$ rm -Rf HHPodTestLibrary
然后在将Git的变动push到远端仓库即可
$ cd ~/.cocoapods/repos/riversea2015_mobile_iphone_repo_local
$ git add --all .
$ git ci -m "remove unuseful pods"
$ git push origin master
常见错误处理 可以参考 这篇文章 的 报错问题 部分,个人觉得这位前辈写的比较全面了。