CocoaPods私有库

CocoaPodsiOS开发中的类库管理工具,项目的类库依赖关系在一个称为Podfile的文本文件中指定

简介

CocoaPods使用独立的版本仓库来管理代码仓库版本,通过更新代码仓库版本来管理代码。CocoaPods的独立的版本仓库github上是这个CocoaPods/Specs,同时本地安装配置好CocoaPods后,在本地~/.cocoapods/repos/master目录下也能查看

开发者通过Cocoapods命令向Specs版本仓库添加自己开源仓库的信息和版本,所以开发者就都可以通过CocoaPods下载使用的开源仓库的代码,除此之外,开发者还可以通过自建私有库管理自己项目内部基础组件代码,这里主要介绍CocoaPods私有库的使用

安装CocoaPods

CocoaPods依赖于Ruby环境,刚好Mac下自带Ruby,使用Rubygem命令即可安装CocoaPods

为防止因gem太老而引发问题,建议执行如下命令先更新gem:

sudo gem update --system

然后执行如下命令安装CocoaPods:

sudo gem install cocoapods

如果执行命令没有反应,可能是被墙了(感觉现在已经不再被墙),替换Ruby镜像源后重新安装即可:

gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/

替换时确保Ruby源只有唯一一个,查看Ruby源的命令是:

gem sources -l

安装完成后需要执行这个命令完成CocoaPods的初始配置:

pod setup

注: pod setup 是Cocoapods将它的信息下载到 ~/.cocoapods/repos 目录下
即使在安装时不执行此命令,在初次执行 pod install 命令时,系统也会自动执行 pod setup

使用CocoaPods

利用CocoaPods,在项目中导入MJExtension类库

为了确定MJExtension是否支持CocoaPods,可以用CocoaPods的搜索功能验证一下。在终端中输入:

pod search MJExtension

cache02

查找到MJExtension是支持CocoaPods的之后,开始使用Podfile文件,Podfile文件描述了项目的类库依赖关系,由开发者自己书写内容。新建一个Test项目,打开终端,cdTest路径下执行:

vim Podfile

cache01

输入以下内容后保存:

platform :ios,'8.0'

target 'Test' do
pod 'MJExtension','~> 3.0.13'
end

安装,执行这个命令:

pod install

cache04

安装成功之后,就可以去项目里面使用,现在的项目结构是这样:

cache05

现在要通过双击Test.xcworkspace打开项目,打开后发现项目里面多了红色框的部分,可以看到MJExtension已经被引入了

cache06

Podfile文件

Podfile中对于约束的描述,其实都可以看作是对代码简写,上面的代码在解析时可以当做Ruby代码来执行。Ruby拥有eval语法,这个方法会将字符串当做代码执行,这样Podfile中的配置项就成了Ruby执行中的数组。这是一个复杂点的例子:

# 下面两行是指明依赖库的来源地址
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/XXX/Specs.git'

# 说明平台是ios,版本是9.0
platform :ios, '9.0'

# 忽略引入库的所有警告(强迫症者的福音啊)
inhibit_all_warnings!

# 针对Test target引入MJExtension
# 针对MyTest target引入OCMock,
target 'Test' do
pod 'MJExtension', '~> 3.0.13'
target 'MyTest' do
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

接下来是一些更多的配置:

Dependencies

Build configurations

默认情况下, 依赖项会被安装在所有targetbuild configuration中。为了调试或者处于其他原因,依赖项只能在给定的build configuration中被启用

下面写法指明只有在DebugBeta模式下才有启用配置

pod 'PonyDebugger', :configurations => ['Debug', 'Beta']

Subspecs

一般情况我们会通过依赖库的名称来引入,CocoaPods会默认安装依赖库的所有内容。
我们也可以指定安装具体依赖库的某个子模块,例如:

# 仅安装 QueryKit 库下的 Attribute 模块
pod 'QueryKit/Attribute'
# 仅安装 QueryKit 下的 Attribute 和 QuerySet 模块
pod 'QueryKit', :subspecs => ['Attribute', 'QuerySet']

从外部引入podspec引入

podspec可以从另一个源库的地址引入

pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec'

target

在给定的块内定义podtargetXcode工程中的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 configuration

inhibit_all_warnings!

inhibit_all_warnings!屏蔽所有来自于CocoaPods依赖库的警告。你可以全局定义,也能在子target里面定义,也可以指定某一个库:

# 隐藏SSZipArchive的警告而不隐藏ShowTVAuth的警告
pod 'SSZipArchive', :inhibit_warnings => true
pod 'ShowTVAuth', :inhibit_warnings => false

use_frameworks!

通过指定use_frameworks!要求生成的是framework而不是静态库

如果使用use_frameworks!命令会在Pods工程下的Frameworks目录下生成依赖库的framework
如果不使用use_frameworks!命令会在Pods工程下的Products目录下生成.a的静态库

私有库

上面介绍的CocoaPods都是使用第三方公有代码库,接下来是使用CocoaPods创建私有库,管理项目内部基础组件代码。这里的私有库包括两个代码库,版本仓库代码仓库

创建版本仓库

创建私人git仓库,这个仓库用来存放版本描述文件,我们选择oschina创建远程私有仓库,然后在终端执行命令:

cache07

pod repo add MyRepo https://git.oschina.net/XXX/MyRepo.git

然后查看在Finder目录~/.cocoapods/repos,可以发现增加了一个MyRepo的储存库

创建代码仓库

回到oschina创建私人代码库,创建时添加MIT LicenseREADME

cache08

将仓库克隆到本地,添加你的代码文件、仓库名.podspec描述文件

.podspec文件是你这个代码库的pod描述文件,可以通过pod指令创建空白模板:

pod spec create MyAdditions

详细配置参考下面的模板:

Pod::Spec.new do |s|
s.name = 'YJDemoSDK' #项目名
s.version = '0.1.0' #相应的版本号
s.summary = 'A short description of YJDemoSDK.' #简述
s.description = <<‐ DESC #详细描述
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/yangjie2/YJDemoSDK' #项目主页
s.license = { :type => 'MIT', :file => 'LICENSE' } #开源协议
s.author = { 'yangjie2' => 'yangjie2@guahao.com' } #作者
s.platform = :ios, '8.0' #支持的平台
s.requires_arc = true #arc和mrc选项
s.libraries = 'z', 'sqlite3' #表示依赖的系统类库,比如libz.dylib等
s.frameworks = 'UIKit','AVFoundation' #表示依赖系统的框架
s.ios.vendored_frameworks = 'YJKit/YJKit.framework' # 依赖的第三方/自己的framework
s.vendored_libraries = 'Library/Classes/libWeChatSDK.a' #表示依赖第三方/自己的静态库(比如libWeChatSDK.a)
#依赖的第三方的或者自己的静态库文件必须以lib为前缀进行命名,否则会出现找不到的情况,这一点非常重要

#平台信息
s.platform = :ios, '7.0'
s.ios.deployment_target = '7.0'

#文件配置项
s.source = { :git => 'https://github.com/yangjie2/YJDemoSDK.git', :tag => s.version.to_s }
#配置项目的目标路径,如果不是本地开发,pod init/update会从这个路去拉去代码

s.source_files = 'YJDemoSDK/Classes/**/*.{h,m}' #你的源码位置
s.resources = ['YJDemoSDK/Assets/*.png'] #资源,比如图片,音频文件等
s.public_header_files = 'YJDemoSDK/Classes/YJDemoSDK.h' #需要对外开放的头文件

#依赖的项目内容 可以多个
s.dependency 'YYModel'
s.dependency 'AFNetworking' '2.3'

#修改xcodebuild的config默认值
s.pod_target_xcconfig = {
'OTHER_LDFLAGS' => '-ObjC -force_load'
}
end

AFNetworking.podspec的详细配置:

Pod::Spec.new do |s|
s.name = 'AFNetworking'
s.version = '3.2.1'
s.license = 'MIT'
s.summary = 'A delightful iOS and OS X networking framework.'
s.homepage = 'https://github.com/AFNetworking/AFNetworking'
s.social_media_url = 'https://twitter.com/AFNetworking'
s.authors = { 'Mattt Thompson' => 'm@mattt.me' }
s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => s.version, :submodules => true }
s.requires_arc = true

s.public_header_files = 'AFNetworking/AFNetworking.h'
s.source_files = 'AFNetworking/AFNetworking.h'

pch_AF = <<-EOS
#ifndef TARGET_OS_IOS
#define TARGET_OS_IOS TARGET_OS_IPHONE
#endif

#ifndef TARGET_OS_WATCH
#define TARGET_OS_WATCH 0
#endif

#ifndef TARGET_OS_TV
#define TARGET_OS_TV 0
#endif
EOS
s.prefix_header_contents = pch_AF

s.ios.deployment_target = '7.0'
s.osx.deployment_target = '10.9'
s.watchos.deployment_target = '2.0'
s.tvos.deployment_target = '9.0'

s.subspec 'Serialization' do |ss|
ss.source_files = 'AFNetworking/AFURL{Request,Response}Serialization.{h,m}'
ss.public_header_files = 'AFNetworking/AFURL{Request,Response}Serialization.h'
ss.watchos.frameworks = 'MobileCoreServices', 'CoreGraphics'
ss.ios.frameworks = 'MobileCoreServices', 'CoreGraphics'
ss.osx.frameworks = 'CoreServices'
end

s.subspec 'Security' do |ss|
ss.source_files = 'AFNetworking/AFSecurityPolicy.{h,m}'
ss.public_header_files = 'AFNetworking/AFSecurityPolicy.h'
ss.frameworks = 'Security'
end

s.subspec 'Reachability' do |ss|
ss.ios.deployment_target = '7.0'
ss.osx.deployment_target = '10.9'
ss.tvos.deployment_target = '9.0'

ss.source_files = 'AFNetworking/AFNetworkReachabilityManager.{h,m}'
ss.public_header_files = 'AFNetworking/AFNetworkReachabilityManager.h'

ss.frameworks = 'SystemConfiguration'
end

s.subspec 'NSURLSession' do |ss|
ss.dependency 'AFNetworking/Serialization'
ss.ios.dependency 'AFNetworking/Reachability'
ss.osx.dependency 'AFNetworking/Reachability'
ss.tvos.dependency 'AFNetworking/Reachability'
ss.dependency 'AFNetworking/Security'

ss.source_files = 'AFNetworking/AF{URL,HTTP}SessionManager.{h,m}', 'AFNetworking/AFCompatibilityMacros.h'
ss.public_header_files = 'AFNetworking/AF{URL,HTTP}SessionManager.h', 'AFNetworking/AFCompatibilityMacros.h'
end

s.subspec 'UIKit' do |ss|
ss.ios.deployment_target = '7.0'
ss.tvos.deployment_target = '9.0'
ss.dependency 'AFNetworking/NSURLSession'

ss.public_header_files = 'UIKit+AFNetworking/*.h'
ss.source_files = 'UIKit+AFNetworking'
end
end

然后执行这个命令来验证我们的仓库配置是否正确:

pod lib lint

一般出现错误警告,需要添加--private或者--allow-warnings,就可以通过验证:

pod lib lint --allow-warnings

验证成功后出现:

-> MyAdditions (0.0.1)

MyAdditions passed validation.

将描述文件推送到版本库

将项目打上标签推到远程代码仓库,标签号和版本号对应都是0.0.1

# 创建标签
git tag -a 0.0.1 -m '标签说明'
# 推送到远程
git push origin --tags

最后将我们的代码仓库的描述信息,push到我们的版本仓库

pod repo push MyRepo MyAdditions.podspec

这时会对远程仓库进行验证,成功的话就会在~/.cocoapods/repos/MyRepo中发现新增的仓库描述信息了

若是出现错误信息:

[!] The repo `MyRepo` at `../.cocoapods/repos/MyRepo` is not clean

更新下我们的版本仓库:

pod repo update MyRepo

再继续上传即可

pod repo push MyRepo MyAdditions.podspec的过程就是

  1. 验证 MyAdditions.podspec 文件
  2. 拉取远程版本仓库 MyRepo
  3. 添加 MyAdditions.podspec 到版本仓库
  4. push 到远程

私人pod库的使用

使用私人pod库的需要在Podflie中添加这句话,指明你的版本仓库地址:

source 'https://git.oschina.net/XXX/MyRepo.git' **注意** 是版本仓库的地址,而不是代码仓库的地址

若有还使用了公有的pod库,需要把公有库地址也带上:

source 'https://github.com/CocoaPods/Specs.git'

最后的Podflie文件变成这个样子:

source 'https://github.com/CocoaPods/Specs.git'
source 'https://git.oschina.net/baiyingqiu/MyRepo.git'

platform :ios, '8.0'

target 'MyPodTest' do
use_frameworks!

pod 'BYPhoneNumTF' #公有库
pod 'MyAdditions' #我们的私有库
pod 'BYAdditions' #这是我又添加到版本库中的另一个代码库

end

然后执行:

pod install

加载完成可以看到代码已经整合到我们的项目中了

执行pod install命令时:

  • 会拉取远程Podfliesource标记版本库到本地的repos文件夹中
  • 在版本库中搜索我们pod 'MyAdditions'MyAdditions.podspec文件
  • 根据MyAdditions.podspec文件中描述的源码地址下载并整合到项目中

注: 与pod install命令类似的还有一个pod update命令,两者需要注意的区别是pod install会使用Podfile.lock文件内的版本,pod update会无视并修改Podfile.lock文件内的版本

Podfile.lock的文件

执行pod install之后,CocoaPods会生成一个名为Podfile.lock的文件。并锁定当前各依赖库的版本,之后如果多次执行pod install或者团队中的其它人check下来这份包含Podfile.lock文件的工程后再执行pod install命令时,获取下来的Pods依赖库的版本就和最开始用户获取到的版本一致。如果没有Podfile.lock文件,执行pod install命令会获取第三方库的最新版本,这就有可能造成同一个团队使用的依赖库版本不一致,这对团队协作的危害无疑是灾难性的

更改Podfile中各依赖库的版本和执行pod update两种操作会修改Podfile.lock文件

鉴于Podfile.lock文件对团队协作如此重要,我们应该将它添加到版本控制里

在gerrit环境下创建私有Pods

gerrit的项目的git地址带有邮箱,而私有pod要求同一个git地址能够被大家都能访问

解决的方法是:

使用Https

利用https地址。例如:https://YOURHOST/apps/Specs-iOS.git
配置密码

gerrit个人页面-HTTP Password,生成http密码
usernamepasswordgit clone需要的用户名和密码

使用ssh

通过配置config文件来统一clone命名,不再需要写用户名
编辑~/.ssh/config文件
添加
Host YOURHOST User YOURNAME

pod repo push中的push这一步会出错

gerrit下,push代码需要通过review审核才能合入,push命令被要求是这样git push origin HEAD:refs/for/master

解决的方法是:

我直接修改了CocoaPods的源码,进入这个/usr/local/lib/ruby/gems/2.5.0/gems/cocoapods-1.5.0/lib/cocoapods/command/repo/push.rb文件,将其中的master修改为了HEAD:refs/for/master来避免出错

def push_repo
UI.puts "\nPushing the `#{@repo}' repo\n".yellow
# 这里 master 修改为 HEAD:refs/for/master 可以避免出错
repo_git('-C', repo_dir, 'push', 'origin', 'master')
end

更新CocoaPods中的第三方们

第三方库们都有人在维护升级,我们需要隔断时间就要更新下我们工程中第三方库的版本。只需要终端输入命令pod update就可以了

如果遇到pod install或者pod update慢的问题,原因在于当执行以上两个命令的时候会升级CocoaPodsspec仓库,加一个参数可以省略这一步,然后速度就会提升不少。加参数的命令如下:

pod install --verbose --no-repo-update
pod update --verbose --no-repo-update

总结

目前是总结网上教程,实践还在探索更新中

来源 看一遍就会的CocoaPods的安装和使用教程
来源 你真的会写Podfile吗?
来源 CocoaPods私有仓库的创建
来源 CocoaPods官网
来源 CocoaPods 都做了什么?
来源 在gerrit环境下创建私有Pods