在上一篇文章CocoaPods,创建属于自己的公开库 中我们讲了如何将自己的框架发布到CocoaPods中供别人来使用。在这一篇文章我将来介绍下如何利用CocoaPods来打造属于自己的远程私有库。
背景
随着公司业务的发展以及产品线的增加,我们需要将APP拆分为若干个基础组件、功能组件和业务组件,以便跨APP使用,并且为了方便管理和维护,对于这些组件我们也想利用CocoaPods来统一管理,但是呢又不想开源让别人使用,只是想让自己内部的团队去使用,这种情况该如何操作呢?下面我们就一步步的带领大家去创建一个属于自己的远程私有仓库。
一在代码托管平台创建私有库
在代码托管平台创建私有仓库,因为GitHub
上私有仓库需要收费,这里以Coding.net
为例,如下图:
二 创建PodSpec文件
据我所知创建PodSpec文件可以通过两种方式,第一种通过 pod spec create xxx
命令来创建,第二种就是通过 pod lib create xxx
命令来创建,相比较之下我更喜欢第二种方式。因为通过第二种方案我们可以选择生成Example工程和测试框架,方便调试以及帮助他人学习使用,更加的方便。
在终端中 cd 到一个空的目录下,运行如下命令:
pod lib create privateLib
CocoaPods会让我们回答几个问题,如下图所示:
把组件相关的类放到 privateLib/Classes 目录中,这是因为 Example 工程生成的 Podfile 文件中指定了这个目录是源文件地址,我们将一个UIView的分类放到该目录下,如下:
然后在终端中 cd 到 Example 目录下并运行 pod install
命令,将我们的宿主工程也就是Example与我们拖入的组件建立依赖关系,这样我们就可以正常的使用我们的Example工程了。
三 发布组件到代码托管平台
在代码托管平台上建立一个私有的仓库
git add .
git commit -m "项目初始化"
git remote add origin https://git.coding.net/Guanzp/privateLib.git
紧接着给我们的组件打上标签,其实也就是版本
git tag -a "0.1.0" -m "Version 0.1.0"
git push --tags
四 编辑 podspec 文件
接下来我们要编辑 podspec 文件,因为在上一篇文章中我们已经详细的介绍过该文件这里就不在赘述。然后通过 pod lib lint
测试 podspec 文件是否可用
五 创建私有Spec库
在 代码托管平台上另外建一个名字为 spec 的仓库,作为我们私有的 podspec 专用仓库,用于存储spec文件, 而不是我们的项目, 然后在本地添加一个新的源
pod repo add privateSpec https://git.coding.net/Guanzp/privateSpec.git
然后我们就会发现在本地多了一个名称为privateSpec的索引库
紧接着将刚才我们编辑好的podspec文件push到本地spec索引库中,同时同步到远程spec库中。
pod repo push privateSpec privateLib.podspec
这样远程仓库和本地索引库中就都有了
五 使用远程私有库
使用方式和其他的开源库一样,但不同的是,需要在头部增加
source 'https://github.com/CocoaPods/Specs.git'#github 官方仓库地址
source 'https://git.coding.net/xxx/privateSpec.git'#自己的远程私有库地址
use_frameworks!
target 'privateLibTest' do
pod 'privateLib'
end
遇到的问题
-
pod lib lint
验证成功了,但是在接下来的pod repo push
报错而且报的是就是podspec
无法通过验证,解决方法,重新提交一个tag并push。在执行pod repo push
,验证通过。
后记
- 组件依赖。有时候我们开发的组件有可能会依赖其它三方的组件,比如我们的组件对
AFNetWorking
和SDWebImage
有依赖:这个时候我们只需要在podspec文件中添加下面两个依赖即可。
s.dependency 'AFNetworking'
s.dependency 'SDWebImage'
- 资源加载问题。当组件中使用到图片等资源文件的时候,可以将图片放到
Assets
文件夹中,如下图:
[图片上传失败...(image-74892f-1516092123165)]
同时,我们需要对podspec
文件进行修改,指定Bundle
资源文件路径:
s.source_files = 'XMGFMMain/Classes/**/*'
s.resource_bundles = {
'XMGFMMain' => ['XMGFMMain/Assets/*']
}
因为图片是存放在组件中,所以需要在当前类所在的Bundle
中去查找图片资源,而不是在MainBundle
中。比如下图:
[图片上传失败...(image-38f09f-1516092123165)]
NSBundle *currentBundle = [NSBundle bundleForClass:[self class]];
NSString *imagePath = [currentBundle pathForResource:@"[email protected]" ofType:nil inDirectory:@"XMGFMMain.bundle"];
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
self.backgroundImage = image;
同理,在组件中加载xib文件
NSBundle *currenBundle = [NSBundle bundleForClass:self];
XMGMiddleView *middleView = [[currenBundle loadNibNamed:@"XMGMiddleView" owner:nil options:nil] firstObject];
- 统一披露API。组件内部提供的服务很多, 如果他人使用, 无法确定有哪些API,如何解决?可以通过统一披露API来解决这个问题。在API中我们需要考虑:需要提供哪些服务给外界;外界需要给你提供哪些服务等;
- 当大家在进行
pod search AFNetWorking
时候,我们会发现它下面有很多Subspaces,其实很容易理解,有时候我们只想用AFNetWorking
中的Reachability
,而又不想将AFNetWorking
整个框架都导入进来。那么我们可不可以模仿它这么做呢?
-> AFNetworking (3.1.0)
A delightful iOS and OS X networking framework.
pod 'AFNetworking', '~> 3.1.0'
- Homepage: https://github.com/AFNetworking/AFNetworking
- Source: https://github.com/AFNetworking/AFNetworking.git
- Versions: 3.1.0, 3.0.4, 3.0.3, 3.0.2, 3.0.1, 3.0.0, 3.0.0-beta.3,
3.0.0-beta.2, 3.0.0-beta.1, 2.6.3, 2.6.2, 2.6.1, 2.6.0, 2.5.4, 2.5.3, 2.5.2,
2.5.1, 2.5.0, 2.4.1, 2.4.0, 2.3.1, 2.3.0, 2.2.4, 2.2.3, 2.2.2, 2.2.1, 2.2.0,
2.1.0, 2.0.3, 2.0.2, 2.0.1, 2.0.0, 2.0.0-RC3, 2.0.0-RC2, 2.0.0-RC1, 1.3.4,
1.3.3, 1.3.2, 1.3.1, 1.3.0, 1.2.1, 1.2.0, 1.1.0, 1.0.1, 1.0, 1.0RC3, 1.0RC2,
1.0RC1, 0.10.1, 0.10.0, 0.9.2, 0.9.1, 0.9.0, 0.7.0, 0.5.1 [master repo]
- Subspecs:
- AFNetworking/Serialization (3.1.0)
- AFNetworking/Security (3.1.0)
- AFNetworking/Reachability (3.1.0)
- AFNetworking/NSURLSession (3.1.0)
- AFNetworking/UIKit (3.1.0)
其实很简单,我们只需要在podspec文件中添加subspec
即可,比如
Pod::Spec.new do |s|
s.name = 'BaseClass'
s.version = '1.1.0'
s.summary = 'BaseClass.这里是一些公共类'
s.subspec 'Base' do |sb|
sb.source_files = 'BaseClass/Classes/Base/**/*'
end
s.subspec 'Category' do |sb|
sb.source_files = 'BaseClass/Classes/Category/**/*'
end
s.subspec 'Network' do |sb|
sb.source_files = 'BaseClass/Classes/Network/**/*'
#因为Network这个类库对AFNetworking、SDWebImage和Category有依赖
sb.dependency 'AFNetworking'
sb.dependency 'SDWebImage'
sb.dependency 'BaseClass/Category'
end
s.subspec 'Tool' do |sb|
sb.source_files = 'BaseClass/Classes/Tool/**/*'
end
当我们打上标签并且执行pod repo push privateSpec privateLib.podspec
之后在终端中执行pod search BaseClass
-> BaseClass (1.1.0)
BaseClass.这里是一些公共类
pod 'XMGFMBase', '~> 1.1.0'
- Homepage: https://coding.net/u/Guanzp/p/BaseClass
- Source: https://git.coding.net/Guanzp/BaseClass.git
- Versions: 1.1.0, 1.0.0, 0.1.0 [privateSpec repo]
- Subspecs:
- BaseClass/Base (1.1.0)
- BaseClass/Category (1.1.0)
- BaseClass/Network (1.1.0)
- BaseClass/Tool (1.1.0)
是不是很神奇?哈哈,假如以后我们只想用BaseClass
中的某一个组件而又不想导入整个BaseClass
,那么我们只需要在podfile
文件中导入该子 组件 即可。