Cocopods GitHub
Cocopods 使用说明

开篇叨叨

一般一款软件都往往由几个人同时协作开发,并且开发期间会使用多个开源库,从而避免重复造轮子,这样可以加快开发的速度,但是这也带来了一个问题:如何管理这些依赖,这是一个十分浪费时间并且容易出错的工作,我们不但要定期管理这些三方库的更新,还需要往目标项目工程上添加三方库所需要的依赖库,对于某些开源库可能还需要添加某些编译参数。稍稍完备一点的编程语言一般都会引入依赖管理工具,来减轻这些负担。比如Java语言的Maven,Nodejs的npm,Android的Gradle,Ruby的gem。iOS也有它的依赖管理工具 — CocoaPods。

CocoaPods 安装
  1. CocoaPods 是使用Ruby 实现的,它使用gem命令进行下载并安装,在安装前最好使用下面的命令来更新下gem
sudo gem update --system
  1. gem是从对应的Ruby软件库中下载对应的软件的,默认情况下使用的是https://rubygems.org它托管在亚马逊云服务上,国内可能会被墙,所以一般会使用国内的源来替换,如果你遇到了这个问题可以使用下面的命令来替换Ruby软件库:
gem sources -l                                  //查看Ruby数据源
gem sources --remove https://rubygems.org/ //移除原有的Ruby软件库
gem sources -a https://ruby.taobao.org/ //使用国内淘宝的Ruby软件库
  1. 安装最新版本的CocoaPods
sudo gem install cocoapods
  1. 安装指定版本的CocoaPods
sudo gem install cocoapods -v 1.4.0
  1. 查看当前本地安装的CocoaPods版本
gem list cocoapods
  1. 卸载当前安装的CocoaPods
sudo gem uninstall cocoapods

有时候卸载不干净可以通过上面的gem list cocoapods列出所有相关的库,并通过下面命令卸载

gem uninstall cocoapods
gem uninstall cocoapods-core
gem uninstall cocoapods-downloader
gem uninstall cocoapods-plugins
gem uninstall cocoapods-search
gem uninstall cocoapods-stats
gem uninstall cocoapods-trunk
gem uninstall cocoapods-try
  1. 初始化CocoaPods repo
pod setup

这一步是比较耗时的,它是将 pod repo 的 镜像索引信息下载到 ~/.cocoapods/repos目录下,如果这个进度实在等得太久了可以试着 cd 到那个目录,用du -sh *来查看下载进度,我们怎么知道我们有哪些repo呢?

可以使用pod repo命令来查看:

master
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/huya/.cocoapods/repos/master

URL表示远程的repo库地址,Path表示下载存放的地址。Type 是代码库类型,上图表示的是从https://github.com/CocoaPods/Specs.git这个地址,将镜像索引下载到本地的/Users/huya/.cocoapods/repos/master 文件夹下。

镜像索引其实是一个配置文件,里面包含了某个库当前的版本,这些库的地址,如下图所示:

我们可以手动添加删除pod repo库

pod repo remove master
pod repo add master https://gitcafe.com/akuandev/Specs.git

当然可以通过在Podfile中通过source来添加,这个后面会举例说明。

一旦setup之后,后续如果需要更新repo库可以通过来更新。

pod repo update
CocoaPods 常用命令行
  1. 初始化Pod
pod init

这时候会在当前目录下新创建一个Podfile,后面将专门介绍如何编写Podfile

  1. 安装依赖
pod install

这时候会参照Podfile从~/.cocoapods/repos/对应的目录下寻找对应库的下载地址并将库下载到Pod文件夹下,然后生成对应的workspace文件,这个在后面介绍原理的时候再详细介绍

  1. 查找对应的pod库
pod search

如果只是开发的话一般上面几个命令就够用了

  1. 使用指定版本执行命令
pod 1.4.0 install
  1. install 的时候输出详细过程Log
pod install –verbose
  1. 更新某一个组件
// 不添加组件名则更新所有
pod update [组件名]
  1. 更新本地依赖

如果 github 或者私有仓库上面有最新版本,本地搜到的还是旧版本。如果 Podfile 中使用新的版本号,这样是无法执行成功的,这时候必须对本地依赖库进行一次更新。

pod repo update
多版本CocoaPods管理

有时候会遇到比如工作中我们协商好都用1.4.0 但是我们自己的某些练习项目需要用到1.8.4 这时候是最头疼的一件事情,为了解决这个问题我目前使用Bundler来管理各个项目的CocoaPods版本:

安装bundler:

gem install bundler

和Cocoapods的Podfile文件一样,我们需要创建一个Gemfile文件,文件位置和Podifle所在位置相同即可,可以使用:

bundle init

在Gemfile文件中,配置所需的Cocoapods版本:

source "https://rubygems.org"
gem 'cocoapods', '1.8.4'

执行bundle install

之后就可以在相应位置,执行bundle exec pod xxxxx 就可以了。

****Podfile ****

最全面的还是官方文档:Podfile 官方文档

一个比较简单的Podfile如下所示

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

platform :ios, '9.0'

target 'IDLFundation' do
# Pods for IDLFundation
end

target 'IDLFundationTest' do
# Pods for IDLFundationTest
end
1. install

这个命令是cocoapods声明的一个安装命令,用于安装引入Podfile里面的依赖库,也就是pod install执行时的一些参数设置,在项目中没有使用过,在这里先不做过多介绍,后面如果有机会会带大家过一下CocoaPods源码,到时候再看下这个配置的作用。

2. source

source 用于指定specs的位置,sources的顺序是有关系的。CocoaPods将使用pod第一次出现的source中的最高版本.cocoapods 官方source是隐式的需要的,如果只有一个cocoapods官方的source则可以省去不写,但是一旦你指定了其他source 你就需要也把官方的指定上。

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

target指明当前的依赖是针对哪个项目target的,可以在target块里面为某个指定的target定义依赖项,一般我们会通过def定义一个依赖集合,然后在不同的target引用,

def common_Pods
#架构基本
pod 'ReactiveObjC', '~> 3.1.1'
pod 'PromiseKit', '~> 1.0'
pod 'Aspects', '~> 1.4.1'
pod 'Objection', '~> 1.6.1'
pod 'BlocksKit', '~> 2.2.5'

//.....
end

platform :ios, '9.0'


target 'IDLFundation' do
common_Pods
# Pods for IDLFundation
end

target 'IDLFundationTest' do
common_Pods
# Pods for IDLFundationTest
//........
pod 'LookinServer', :configurations => ['Debug']
pod 'Reveal-SDK', :configurations => ['Debug']
end
4. platform

platform指定了静态库应该被编译在哪个平台.下面是各个平台platform的默认值。

iOS -> 4.3
OS X -> 10.6
tvOS -> 9.0
watchOS -> 2.0
5. inhibit_all_warnings

inhibit_all_warnings! 用于屏蔽cocoapods库里面的所有警告,它也可以用于屏蔽某个库的编译警告

pod 'SSZipArchive', :inhibit_warnings => true

6. use_frameworks

use_frameworks用于告诉CocoaPods我们想使用Frameworks而不是Static Libraries。不显式指定的话会默认使用Static Libraries,会在Pods工程下的Products目录下生成.a的静态库,如果指定的话会在Pods工程下的Frameworks目录下生成依赖库的framework,由于Swift不支持静态库,所以如果项目中使用到Swift库的话就必须使用use_frameworks!,纯OC项目是不用use_frameworks的。

7. Pod

指定每个target的依赖项

  • 如果后面不写依赖库的具体版本号,那么cocoapods会默认选取最新版本,一般不推荐使用这种方式,因为库升级的时候可能会带来不兼容或者其他怪异的问题,一般我们项目中都不会使用这种方式,而是在使用库的时候明确指明库对应的版本,如果要升级库也要我们明确,库的升级是否会给我们带来问题,并通过测试后才能升级发布,否则一个项目中有几十个库,很难追踪问题是哪个库升级导致的。
pod 'SSZipArchive'
  • 要特定的依赖库的版本,只需要在后面写上具体版本号即可:
pod 'Objection', '0.9'
  • 当然除了指定特定版本外还可以通过下面的来指定版本范围,但是个人还是推荐明确指明所依赖的库的版本。
* > 0.1 高于0.1版本(不包含0.1版本)的任意一个版本
* >= 0.1 高于0.1版本(包含0.1版本)的任意一个版本
* < 0.1 低于0.1版本(不包含0.1版本)的任意一个
* <= 0.1低于0.1版本(包含0.1版本)的任意一个
* ~> 0.1.2 版本 这个例子等效于>= 0.1.2并且 <0.2.0,并且始终是你指定范围内的最新版本,一般同一个版本簇的API都是需要兼容的,所以这也是一个比较合理的方式,但是很难避免某些三方库的开发人员没有这方面的考虑和支持,因此还是推荐明确指定使用的版本。
  • pod 中还可以指定当前依赖只在某些给定的build configuration中被启用,比如:
pod 'LookinServer', :configurations => ['Debug']
  • 正常情况下我们会通过依赖库的名称来引入,但是有些依赖库是有多个子依赖的,比如:
pod 'Firebase/Analytics'
pod 'Firebase/Performance'
pod 'Firebase/RemoteConfig'

这种除了使用上面写法外,还可以通过Subspecs来代替,但是个人还是推荐上面的写法比较简单:

pod 'Firebase', :subspecs => ['RemoteConfig']
  • 使用本地依赖
pod 'AFNetworking', :path => '~/Documents/AFNetworking'
  • 使用指定远程依赖

引入master分支(默认)

pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git'

引入指定的分支

pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :branch => 'dev'

引入某个Tag标签的代码

pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :tag => '0.7.0'

引入某个特殊的提交节点

pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :commit => '082f8319af'
  • 从另一个源库引入
pod 'PonyDebugger', :source => 'https://github.com/CocoaPods/Specs.git'
pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec'

podspec 用于指定podspec的信息:

# 不指定表示使用根目录下的podspec,默认一般都会放在根目录下,并且使用库名作为名称
podspec
# 如果podspec的名字与库名不一样,可以通过这样来指定
podspec :name => 'DemoPodSpect'
# 如果podspec不是在根目录下,那么可以通过:path来指定路径
podspec :path => '/Documents/PrettyKit/PrettyKit.podspec'
8. project

用于指定当前target要应用到哪个project,这个用在多project的情况下,如果没有指定说明该target要应用到Podfile目录下与target同名的工程

# MusicApp这个target只有在MyMusic工程中才会链接
target 'MusicApp' do
project 'MyMusic'
...
end

# NotesApp这个target只有在MyNotes工程中才会链接
target 'NotesApp' do
project 'MyNotes'
...
end
9. workspace

默认情况下不需要指定,直接使用与Podfile所在目录的工程名一样就可以了。如果要指定另外的名称,而不是使用工程的名称,可以使用workspace来指定:

workspace 'MyWorkspace'
10. def

def命令来声明一个pod集, 然后在需要引入的target中引入:

def common_Pods
#架构基本
pod 'ReactiveObjC', '~> 3.1.1'
pod 'PromiseKit', '~> 1.0'
pod 'Aspects', '~> 1.4.1'
pod 'Objection', '~> 1.6.1'
pod 'BlocksKit', '~> 2.2.5'

//.....
end

platform :ios, '9.0'

target 'IDLFundation' do
common_Pods
# Pods for IDLFundation
end

target 'IDLFundationTest' do
common_Pods
# Pods for IDLFundationTest
//........
pod 'LookinServer', :configurations => ['Debug']
pod 'Reveal-SDK', :configurations => ['Debug']
end
11. pre_install

这个允许用户在Pods下载完成,但还未安装前对Pods做一些修改,它有一个唯一参数Pod::Installer

pre_install do |installer|
#......
end
12. post_install

当我们安装完成,但是生成的工程还没有写入磁盘之时,我们可以指定要执行的操作。比如,我们可以在写入磁盘之前,修改一些工程的配置:
这两个在项目中暂时没有遇到过。

post_install do |installer| installer.pods_project.targets.each do |target| 
target.build_configurations.each do |config|
config.build_settings['GCC_ENABLE_OBJC_GC'] = 'supported'
end
end
end

下面是一个比较全的一个Podfile模版,供大家参考:

source 'https://github.com/CocoaPods/Specs.git' # 组件依赖文件所存放仓库,根据需求可引入多个
source 'https://github.com/artsy/Specs.git'

platform :ios, '8.0' #
inhibit_all_warnings! # 忽视引用的代码中的警告
workspace 'CocoaPodsDemo' # 指定生成的 workspace 名字

def common_pods # 如果有多个 target,可以将公共部分进行 def 定义再引入
pod 'xxx'
end

target 'CocoaPodsDemo' do
project 'DemoProject' # 可用于指定实际的工程
use_frameworks! # 是否以 framework 形式引入。swift 必须有这个关键字
common_pods # 公共引入的组件
pod 'SSipArchive', :inhibit_warnings => true # 屏蔽某个 pod 的 warning
pod 'AFNetworking', '3.2' # 使用 3.2 版本
pod 'YYCache', '~> 0.3' # pod update 时最高升级到 < 1.0,不包括 1.0

# Build 环境配置
pod 'PonyDebugger', :configurations => ['Debug', 'Beta']
pod 'PonyDebugger', :configuration => 'Debug'

# 使用具体的某个 subspec
pod 'QueryKit/Attribute'
pod 'QueryKit', :subspecs => ['Attribute', 'QuerySet']

# 引用本地组件
pod 'AFNetworking', :path => '~/Documents/AFNetworking'

# 使用具体仓库
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git'
# 使用具体仓库具体分支
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :branch => 'dev'
# 使用具体仓库的某个 tag
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :tag => '0.7.0'
# 使用具体仓库的某个 commit
pod 'AFNetworking', :git => 'https://github.com/gowalla/AFNetworking.git', :commit => '082f8319af'

# 使用指定路径的 spec 文件
pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec'

target 'ShowsApp' do
pod 'ShowsKit'

# Has its own copy of ShowsKit + ShowTVAuth
target 'ShowsTV' do
pod 'ShowTVAuth'
end

# Has its own copy of Specta + Expecta
# and has access to ShowsKit via the app
# that the test target is bundled into
target 'ShowsTests' do
# inherit! 有三种类型:':complete' 继承父级所有行为;':none' 什么行为都不继承;':search_paths' 继承父级的 search paths
inherit! :search_paths
pod 'Specta'
pod 'Expecta'
end
end
end

# hook 配置, 在 preparing 阶段后,install 之前
pre_install do |installer|

end

# hook 配置,在 pod install 之后,可用于修改工程配置等
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['GCC_ENABLE_OBJC_GC'] = 'supported'
end
end
end

CocoaPods 工作原理

关于CocoaPods的工作原理的细节大家可以翻看下CocoaPods的源码看下整个流程:
下面推荐几篇CocoaPod源码解析文章给大家,大家可以结合这些文章对源码进行解析:

这里只想从使用者的角度来介绍下CocoaPods工作原理,主要包括

  1. CocoaPods的组成
  2. CocoaPods的工作流
  3. pod install,pod update,Podfile.lock,Manifest.lock
  • CocoaPods的组成

CocoaPods 本身是由Ruby编写的,它是由多个Ruby包组成的,最主要包括CocoaPods Core,CocoaPods Downloader,Xcodeproj,CLAide,Molinillo这些Ruby包,这些包是通过gem进行管理的,当我们执行pod 命令的时候就会调用对应的模块完成对应的任务,下面是对应模块的源码地址,作用,以及对应的帮助文档,大家在遇到问题的时候可以查找这些资料来解决问题。

模块 说明 帮助文档
CocoaPods/Specs 三方库Podspec文件托管仓库 Doc
CocoaPods CocoaPod 命令行工具 Guide
CocoaPods Core CocoaPods 相关文件(Specification,Podfile,Source)的处理 Guide
CocoaPods Downloader 支持git/SVN/等多种协议的代码下载器 Guide
Xcodeproj 创建和修改Xcode projects文件 Guide
CLAide 命令行接口框架 Guide
Molinillo 通用依赖解析器 Guide
  • CocoaPods的工作流

整个CocoaPods涉及到了三方组件库的开发者,CocoaPods Repo,三方组件库的使用者,三方面对象,整个关系如下图所示:

  1. 首先三方组件库的开发者编写完三方库的代码后会将组件代码上传到GitHub/SVN等代码托管仓库,然后创建一个 podspec 文件,该文件包含了该组件包含的代码及资源,以及依赖关系,版本信息,以及该组件的存储地址,然后将这个podspec推送到CocoaPods共有仓库或者私有Spec管理仓库。

  2. 在运行pod setup或者pod install的时候会查看source指令,将source指令所指定的repo 下载到/.cocoapods/repos目录下。这个文件夹下包含了各个三方组件对应版本以及该版本的podspec文件和podspec.json文件,后续需要下载的时候就可以根据podspec中指定的下载路径下载三方组件了。如果有一个第三方库发布了一个最新的版本,如果不执行pod repo update,那么本地是不会知道有一个最新版本的,还一直以本地的资源目录为准。那么我们永远都拿不到这个库的最新版本,但是有时候我们不执行pod repo update发现也可以拿到最新的库,那是因为pod update会先拉取远程最新目录,再根据目录中的资源重新更新一遍pod,但是如果podfile没有为每个库指定明确的版本,那么每次都会拉取一遍最新库,这时候如果不想每次都拉取,可以使用pod update –no-repo-update。正常情况下pod repo update 会将/.cocoapods/repos/下的所有组件库都更新一遍,如果只想更新某个私有库那么只需要带上需要更新的文件夹就可以只更新某个repo了。

    pod repo update ~/.cocoapods/repos/XXX/
  3. 三方组件库使用者要使用某个组件,需要在Podfile中指定组件名字,版本,repo 源,然后运行pod install命令,CocoaPods 会首先使用eval运行Podfile,并开始解析Podfile。

    pod install可以分成如下阶段

  • prepare 准备阶段
    在该阶段首先会先检查当前运行目录是否是项目根目录,为啥要在根目录?因为pod init的时候需要从.xcodeproj中获取target信息,这就导致了生成的Podfile在项目根目录,而pod install需要Podfile所以也必须需要在根目录。接着检查Podfile.lock文件cocoapods和当前的cocoapods版本是否一致,Podfile中的plugin插件是否已经安装完成并加载,一旦这些检查完毕就会创建Pods以及子目录,并运行pre_install。

  • resolve_dependencies 解决依赖冲突
    这个阶段主要是解析podfile文件中的pod 以及 target等信息以及之间的关系,存储到对应的数据结构中。如果Podfile中有删除的库, 先进行文件清理。

  • download_dependencies 下载依赖
    这个阶段会拿着组件名,组件版本到~/.cocoapods/repos/中去寻找对应的podspec,在podspec文件中找到三方组件存放的地址,从而从远程下载。当然不是每个都需要下载,如果某个组件已经下载,并且版本没有变化的情况下就不会从远程下载。

  • validate_targets target校验
    这阶段将会对下载的依赖进行校验,比如校验是否有多重引用framework 或者 library 的情况,检查不同target所使用的swift版本是否相同,如果使用swift的情况下,检查Podfile是否添加了use_frameworks!。

  • generate_pods_project 生成 Pod project 文件
    这阶段将会生成Pods.xcodeproj工程文件,并将下载的依赖文件,Library 加入工程,处理 target 依赖,并将项目文件以及Pods项目文件添加到新生成的.xcworkspace文件。

  • pod install,pod update,Podfile.lock,Manifest.lock

其实上面已经对这部分内容有所介绍了,这里将这些内容放在一起对比下会更加明显:

在项目第一次使用Cocoapods时候,或者在podfile文件中增加或者删除某个组件的时候需要使用pod install而不是pod update。

当运行pod install,它只解析Podfile.lock中没有的pod的依赖库.对于Podfile.lock中已经有的组件库, Podfile.lock不会尝试检查~/.cocoapods/repos是否有更新的版本.对于没有在Podfile.lock中列出的组件库,pod会搜索与Podfile匹配的版本或最新的版本,并将每个组件已经安装的版本写入到Podfile.lock中.也就是说Podfile.lock 的功能是用于跟踪每个组件的已安装版本并锁定这些版本。

简单说:pod install会优先考虑Podfile里指定的版本信息,其次考虑Podfile.lock 里指定的版本信息来安装对应的依赖库,而不会每次都考虑~/.cocoapods/repos的最新版本

当运行pod update的时候CocoaPods将尝试查找更新的组件版本, 并且会忽略掉Podfile.lock中已经存在的版本.

在多人协作的项目中一般需要将Podfile.lock文件提交到版本控制库中,这样大家就会保证同一时刻使用的三方组件库是一致的,只有在确认某个更新需要同步的时候,某人运行pod update后将Podfile.lock再次提交到代码仓库,其他人拉取到这个最新的Podfile.lock的时候就会提示需要更新,这时候其他人就也需要使用pod update 更新本地的三方组件,但是不论怎样,整个团队的三方组件库总是一致的。

上面介绍了Podfile.lock 那么 Manifest.lock 的作用又是什么?Manifest.lock 是 Podfile.lock 的副本,每次只要生成 Podfile.lock 时就会生成一个一样的 Manifest.lock 存储在 Pods 文件夹下。在每次项目 Build 的时候,会跑一下脚本检查一下 Podfile.lock 和 Manifest.lock 是否一致。也就是说Manifest.lock是用于记录本地/Pod目录下各个组件库的版本信息,那不是和Podfile.lock重复了么?不是的,因为一般我们会将Podfile.lock放到代码仓库中,这部分是有可能其他人远程改动后,我们拉下来,这时候有可能导致Podfile.lock中指定的组件版本和本地的组件版本不一致,那么我们怎么发现这种不一致呢?靠Podfile?这是不可能的,一般就是因为Podfile改了,pod update后导致Podfile.lock发生改动,这样如果没有Manifest.lock 就难以判断远程的是否和本地的版本有差异了,也就是说Manifest.lock 用于标记本地sandbox中三方组件的版本,这也是为什么有了Podfile.lock之后还需要Manifest.lock 的原因了。

CocoaPods 私有库/共有库 制作依赖库
1 创建组件库工程

将代码clone到本地,在根目录运行:

pod lib create IDLUtils

会自动引导我们创建一个组件库:

这时候会生成IDLUtils.podspec文件,文件内容如下:

#
# 在提交前需要先运行 pod lib lint IDLUtils.podspec 来对当前文件进行校验,并且删除包括所有无用的注释。
#
# 如果有关于Podspec 的可以查看 https://guides.cocoapods.org/syntax/podspec.html 文档
#
#

Pod::Spec.new do |s|

# 组件名
s.name = 'IDLUtils'
# 组件版本号,命名规则遵循 https://semver.org/
s.version = '0.0.1'
# 概要
s.summary = 'IDLUtils is a Collection of Utils Help to speed up your develop.'

# 这个描述会出现在搜索结果上,因此我们可以描述以下信息:
#
# * 这个库的功能是什么?为什么要实现它,它的关注点在哪里
# * 尽量简单明了.
# * 在下面的DESC分隔符之间写下描述
# * 最后,不要担心缩进,CocoaPods会自动删除它

s.description = "IDLUtils is a Collection of Utils Help to speed up your develop. Hope you like it "

# 仓库主页
s.homepage = 'http://coderlin.coding.me'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
# 遵循的协议
s.license = { :type => 'MIT', :file => 'LICENSE' }
# 作者
s.author = { 'tbfungeek' => 'tbfungeek@163.com' }
# 在线源码仓库
s.source = { :git => 'https://github.com/tbfungeek/IDLUtils.git', :tag => s.version.to_s }
# 作者社交联系地址
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

# 目标版本
s.ios.deployment_target = '9.0'
# 源文件
s.source_files = 'IDLUtils/Classes/**/*'

#资源
# s.resource_bundles = {
# 'IDLUtils' => ['IDLUtils/Assets/*.png']
# }

#公开的头文件
# s.public_header_files = 'Pod/Classes/**/*.h'
#依赖的库
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
end

下面是pod spec create 创建的大家可以对比下,这种方式生成的选项更多,但是pod lib create 对于简单的组件已经够用了。

#
# 在提交前需要先运行 pod spec lint IDLKeyChainTool.podspec 来对当前文件进行校验,并且删除包括所有无用的注释。
#
# 如果要查看Podspec的属性可以查看:http://docs.cocoapods.org/specification.html
#

Pod::Spec.new do |s|

# ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 这些信息主要用于方便使用者能够快速查找到我们这个组件库,所以最好写得简单明了
#
# 组件名
s.name = "IDLKeyChainTool"
# 组件版本号,命名规则遵循 https://semver.org/
s.version = "0.0.1"
# 概要
s.summary = "A short description of IDLKeyChainTool."

# 这个描述会出现在搜索结果上,因此我们可以描述以下信息:
#
# * 这个库的功能是什么?为什么要实现它,它的关注点在哪里
# * 尽量简单明了.
# * 在下面的DESC分隔符之间写下描述
# * 最后,不要担心缩进,CocoaPods会自动删除它
# 详细描述,很重要的
s.description = "This is a KeyChain Tool for Objective C"
# 仓库主页
s.homepage = "http://EXAMPLE/IDLKeyChainTool"
# 展示截图
# s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"

# ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 当前组件库所使用的证书,可以查看 http://choosealicense.com 对应的说明,CocoaPods将会查看对应的组件库是否有文件名为LICENSE*的文件
# 比较常见的有 'MIT', 'BSD''Apache License, Version 2.0' 类型
#
# 许可证
s.license = "MIT (example)"
# s.license = { :type => "MIT", :file => "FILE_LICENSE" }

# ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 这里可以指定作者名称,邮箱,以及社交网站主页地址
#
s.author = { "Xiaohai.lin" => "tbfungeek@163.com" }
# Or just: s.author = "Xiaohai.lin"
# s.authors = { "Xiaohai.lin" => "tbfungeek@163.com" }
# s.social_media_url = "http://twitter.com/Xiaohai.lin"

# ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 这里可以指定对应的平台信息
#
# s.platform = :ios
s.platform = :ios, "9.0"

# When using multiple platforms
# s.ios.deployment_target = "5.0"
# s.osx.deployment_target = "10.7"
# s.watchos.deployment_target = "2.0"
# s.tvos.deployment_target = "9.0"
# ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 这里用于指定到哪里获取代码,CocoasPods支持git, hg, bzr, svn, HTTP等协议的代码托管地址
#

s.source = { :git => "https://github.com/tbfungeek/IDLKeyChainTool.git", :tag => "#{s.version}" }

# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# CocoaPods 中一般使用一个文件夹作为代码文件夹,文件夹下可以包含
# swift, h, m, mm, c/cpp文件,只要在s.source_files中指定就好
# s.exclude_files中指定的文件会被剔除,而s.public_header_files文件将会对全局可见
#

s.source_files = "Classes", "Classes/**/*.{h,m}"
s.exclude_files = "Classes/Exclude"
# 引入的共有头文件
# s.public_header_files = "Classes/**/*.h"
# 引入的私有头文件
#spec.private_header_files = 'Headers/Private/*.h'

# ――― Resources ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
#
# 下面指定的资源将会通过build phase脚本拷贝到指定的bundle。
#

# s.resource = "icon.png"
# s.resources = "Resources/*.png"
# s.preserve_paths = "FilesToSave", "MoreFilesToSave"

# ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 该组件库相关的frameworks以及libraries,其中libraries不用包括它的名字前缀
#

# s.framework = "SomeFramework"
# s.frameworks = "SomeFramework", "AnotherFramework"

# s.library = "iconv"
# s.libraries = "iconv", "xml2"

# ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 在这里可以指定对应的编译选项以及依赖的三方库
#

s.requires_arc = true
# s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
# s.dependency "JSONKit", "~> 1.4"

# ――― Subspecs ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 将组件分为多个子组件,接入方可以根据需求只接入几个子组件,减少包体积
#subspec 'Twitter' do |sp|
# sp.source_files = 'Classes/Twitter'
#end

# 测试组件
#spec.test_spec do |test_spec|
# test_spec.source_files = 'NSAttributedString+CCLFormatTests.m'
# test_spec.dependency 'Expecta'
#end
# 默认子组件。也就是当接入方不作区分时,直接使用组件名引入时,所引入子组件
#spec.default_subspec = 'Core'
end

 

1 创建GitHub 代码仓库

这里需要创建两个GitHub库,一个是Spec仓库,一个是组件代码仓库,我们这里先介绍组件代码仓库,最后的时候会给大家介绍Spec仓库:

首先创建一个代码仓库用于存放组件代码,如下所示:

https://github.com/tbfungeek/IDLUtils.git

git add .
git commit -m "xxxxxxxxxx"
git remote add origin https://github.com/tbfungeek/IDLUtils.git
//git pull --rebase origin master
//解决冲突
git push -u origin master

将组件代码push到远端仓库,新建tag

git tag 0.0.1

将本地的tag推送到远程仓库

git push --tags

注意tag号要和s.version保持一致

在上面工作完成后运行pod lib lint IDLUtils.podspec对podspec文件进行校验。如果遇到有问题可以通过****–verbose****来看详细的过程。

下面是可能会遇到的导致校验不过的可能问题:

情景一:

[!] IDLUtils did not pass validation, due to 1 warning (but you can use `--allow-warnings` to ignore it).
You can use the `--no-clean` option to inspect any issue.

这种情况下通过****–verbose**** 看下如果warning没问题可以通过****–allow-warnings**** 忽略错误。

情景二:

.podspec error - source_files` pattern did not match any file

这种一般是只是将文件放置到Class目录,没有将Class添加到XCode的引用关系中。

情景三:

Could not find a `ios` simulator, Ensure that Xcode -> Window -> Devices has at least on

升级cocoaPods版本。

情景四:

[!] Found multiple specifications

将私有仓库拉到本地时可能会存在两个。移除重复的库。

有时候你会发现什么都对,但是结果不对,你就可以进入对应的缓存中查看下实际的内容是怎样的?或者使用下面的命令清除缓存看下:

rm ~/Library/Caches/CocoaPods/search_index.json
rm -fr ~/Library/Caches/CocoaPods/Pods/External/IDLUtils
rm -fr ~/Library/Caches/CocoaPods/Pods/Specs/External/IDLUtils/
3 注册CocoaPods
pod trunk register tbfungeek@163.com 'tbfungeek' --description='tbfungeek'

这时候注册邮箱会收到一份验证邮件,通过邮件链接可以完成注册

4 将podspec push 到 CocoaPods Specs
pod trunk push IDLUtils.podspec
创建私有Spec 仓库
1.新建私有仓库

目前github也支持私有仓库了,所以可以在github,gitlab上创建一个。

https://github.com/tbfungeek/IDLPodSpecs.git
2.将私有仓库添加到本地
pod repo add IDLPodSpecs https://github.com/tbfungeek/IDLPodSpecs.git

这时候会将IDLPodSpecs clone 到 ~/.cocoapods/repos 目录

3. 提交 podspec 至私有 Spec 仓库
pod repo push IDLPodSpecs IDLUtils.podspec
4. 在项目中应用该私有库

在Podfile 头部添加repo 源

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

接下来就可以使用了

更深入学习

学习了上面的技术够一般项目使用了,但是随着项目推进,你会发现项目编译速度会越来越慢,甚至达到难以容忍的地步,这时候就需要进行CocoaPods 的组件二进制化,这样就省去了各个组件库的编译速度。这个会在后续章节中专门开一篇博客进行介绍,大家如果感兴趣可以事先了解下,下面是一篇个人认为写得比较好的一篇博客,推荐给大家。

基于 CocoaPods 的组件二进制化实践
Google – CocoaPods 的组件二进制化

Contents
  1. 1. 开篇叨叨
  2. 2. CocoaPods 安装
  3. 3. CocoaPods 常用命令行
  4. 4. 多版本CocoaPods管理
  5. 5. ****Podfile ****
    1. 5.1. 1. install
    2. 5.2. 2. source
    3. 5.3. 3. target
    4. 5.4. 4. platform
    5. 5.5. 5. inhibit_all_warnings
    6. 5.6. 6. use_frameworks
    7. 5.7. 7. Pod
    8. 5.8. 8. project
    9. 5.9. 9. workspace
    10. 5.10. 10. def
    11. 5.11. 11. pre_install
    12. 5.12. 12. post_install
  6. 6. CocoaPods 工作原理
  7. 7. CocoaPods 私有库/共有库 制作依赖库
    1. 7.1. 1 创建组件库工程
    2. 7.2. 1 创建GitHub 代码仓库
    3. 7.3. 3 注册CocoaPods
    4. 7.4. 4 将podspec push 到 CocoaPods Specs
  8. 8. 创建私有Spec 仓库
    1. 8.1. 1.新建私有仓库
    2. 8.2. 2.将私有仓库添加到本地
    3. 8.3. 3. 提交 podspec 至私有 Spec 仓库
    4. 8.4. 4. 在项目中应用该私有库
  9. 9. 更深入学习