Cocoapods私有库搭建

Cocoapods是非常好用的一个iOS依赖管理工具,使用它可以方便的管理和更新项目中所使用到的第三方库,以及将自己的项目中的公共组件交由它去管理。

整体先说明一下创建一个私有的podspec包括如下那么几个步骤:

  1. 创建并设置一个私有的Spec Repo
  2. 创建Pod的所需要的项目工程文件,并且有可访问的项目版本控制地址。
  3. 创建Pod所对应的podspec文件。
  4. 本地测试配置好的podspec文件是否可用。
  5. 向私有的Spec Repo中提交podspec
  6. 在个人项目中的Podfile中增加刚刚制作的好的Pod并使用。
  7. 更新维护podspec

在这一系列的步骤中需要创建两个Git仓库,分别是第一步和第二步(第二步不一定非要是Git仓库,只要是可以获取到相关代码文件就可以,也可以是SVN的,也可以说zip包,区别就是在podspec中的source项填写的内容不同),并且第一步只是在初次创建私有podspec时才需要,之后在创建其他的只需要从第二步开始就可以。

本文只介绍在Git环境下的操作,其他环境其他方式不做讨论。

创建私有Spec Repo

先来说第一步,什么是Spec Repo?他是所有的Pods的一个索引,就是一个容器,所有公开的Pods都在这个里面,他实际是一个Git仓库remote端。

GitHub上,但是当你使用了Cocoapods后他会被clone到本地的~/.cocoapods/repos目录下,可以进入到这个目录看到master文件夹就是这个官方的Spec Repo了。这个master目录的结构是这个样子的:

├── Specs
    └── [SPEC_NAME]
        └── [VERSION]
            └── [SPEC_NAME].podspec

因此我们需要创建一个类似于master的私有Spec Repo

这里我们可以fork官方的Repo,也可以自己创建,个人建议不fork,因为我们只是想添加自己的Pods,没有必要把现有的公开Podscopy一份。

所以创建一个 Git仓库,这个仓库可以创建私有的也可以创建公开的,不过既然私有的Spec Repo,还是创建私有的仓库吧,需要注意的就是如果项目中有其他同事共同开发的话,你还要给他这个Git仓库的权限。因为GitHub的私有仓库是收费的,所以建议使用其他Git服务,比如CODING,当然还有其他的可供选择开源中国、Bitbucket以及CSDN。

创建完成之后在终端中执行如下命令:

# pod repo add [Private Repo Name] [GitHub HTTPS clone URL]
$ pod repo add DemoSpecs https://git.dev.tencent.com/iDog/DemoSepcs.git

此时如果成功的话进入到~/.cocoapods/repos目录下就可以看到DemoSpecs这个目录了。至此第一步创建私有Spec Repo完成。

如果有其他合作人员共同使用这个私有Spec Repo的话在他有对应Git仓库的权限的前提下执行相同的命令添加这个Spec Repo即可。

创建Pod项目工程文件

这个第二步没有什么好介绍的,如果是有现有的组件项目,并且在Git的版本管理下,那么这一步就算完成了,可以直接进行下一步了。

不过我们的组件还在你冗余庞大的项目中,需要拆分出来或者需要自己从零开始创建一个组件库,那么建议使用Cocoapods提供的一个工具将第二步与第三步结合起来做。

现在来说一下这个工具,相关的文档介绍是Using Pod Lib Create ,就拿我创建的podTestLibrary为例子具体讲一下这里是如何操作的,先cd到要创建项目的目录然后执行:

$ pod lib create DSButton

之后他会问你几个问题:

To get you started we need to ask a few questions, this should only take a minute.

2019-08-20 11:11:49.065 defaults[30170:835168]
The domain/default pair of (org.cocoapods.pod-template, HasRunBefore) does not exist
If this is your first time we recommend running through with the guide:
 - https://guides.cocoapods.org/making/using-pod-lib-create.html
 ( hold cmd and click links to open in a browser. )

 Press return to continue.


What platform do you want to use?? [ iOS / macOS ]
 > iOS

What language do you want to use?? [ Swift / ObjC ]
 > ObjC

Would you like to include a demo application with your library? [ Yes / No ]
 > Yes

Which testing frameworks will you use? [ Specta / Kiwi / None ]
 > None

Would you like to do view based testing? [ Yes / No ]
 > No

What is your class prefix?
 > DS

问题和答案如上。

问完这几个问题他会自动执行pod install命令创建项目并生成依赖。

$ tree DSButton -L 2
DSButton
├── Example                                  #demo APP
│   ├── DSButton
│   ├── DSButton
│   ├── DSButtonxcworkspace
│   ├── Podfile                              #demo APP 的依赖描述文件
│   ├── Podfile.lock
│   ├── Pods                                 #demo APP 的依赖文件
│   └── Tests
├── LICENSE                                  #开源协议 默认MIT
├── Pod                                            #组件的目录
│   ├── Assets                             #资源文件
│   └── Classes                             #类文件
├── DSButton.podspec              #第三步要创建的podspec文件
└── README.md                           #markdown格式的README

9 directories, 5 files

接下来就是向Pod文件夹中添加库文件和资源,并配置podspec文件,我把一个DSButton的共有组件放入Pod/Classes中,然后进入Example文件夹执行pod update命令,再打开项目工程可以看到,刚刚添加的组件已经在Pods子工程下Development Pods/DSButton中了,然后编辑demo工程,测试组件,我并没有使用提供的测试框架进行测试,这里就先不介绍了。

这里需要注意的是每当你向Pod中添加了新的文件或者以后更新了podspec的版本都需要重新执行一遍pod update命令。

测试无误后需要将该项目添加并推送到远端仓库,并编辑podspec文件。

通过Cocoapods创建出来的目录本身就在本地的Git管理下,我们需要做的就是给它添加远端仓库,同样去GitHub或其他的Git服务提供商那里创建一个私有的仓库,拿到SSH或者HTTP地址,然后cdDSButton目录。

$ git add .
$ git commit -s -m "Initial Commit of Library"
$ git remote add origin https://git.dev.tencent.com/iDog/DSButton.git       #添加远端仓库
$ git push origin master     #提交到远端仓库

因为podspec文件中获取Git版本控制的项目还需要tag号,所以我们要打上一个tag

$ git tag -m "first release" 0.1.0
$ git push --tags     #推送tag到远端仓库

做完这些就可以开始编辑podspec文件了,它是一个Ruby的文件,把编辑器的格式改成Ruby就能看到语法高亮,下面我贴上我的podspec文件,并在后面以注释的形式说明每个字段的含义,没有涉及到的字段可以去官方文档查阅

# Be sure to run `pod lib lint podTestLibrary.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             = 'DSButton'
  s.version          = '0.1.0'
  s.summary          = 'A short description of podTestLibrary.'

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!

  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC

  s.homepage         = 'https://dev.tencent.com/u/iDog/p/DSButton'
  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'iDog' => 'idoghuan@163.com' }
  s.source           = { :git => 'https://git.dev.tencent.com/iDog/DSButton.git', :tag => s.version.to_s }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.ios.deployment_target = '8.0'

  s.source_files = 'podTestLibrary/Classes/**/*'

  # s.resource_bundles = {
  #   'podTestLibrary' => ['podTestLibrary/Assets/*.png']
  # }

  # s.public_header_files = 'Pod/Classes/**/*.h'
  # s.frameworks = 'UIKit', 'MapKit'
  # s.dependency 'AFNetworking', '~> 2.3'
end

编辑完podspec文件后,需要验证一下这个文件是否可用,如果有任何WARNING或者ERROR都是不可以的,它就不能被添加到Spec Repo中,不过Xdode的WARNING是可以存在的,验证需要执行一下命令

pod spec lint xxx.podspec --verbose

如果输出如下:

DSButton.podspec passed validation.

说明验证通过了,不过这只是这个podspec文件是合格的,不一定说明这个Pod是可以用的,我们需要在本地做一下验证,这就是第四步的内容了,第四步在具体说明。

创建podspec文件

如果从第二步过来,已经有了现成的项目,那么就需要给这个项目创建一个podspec文件,创建它需要执行Cocoapods的另外一个命令,官方文档在这里

$ pod spec create DemoSepcs  git@git.dev.tencent.com:iDog/DemoSepcs.git

执行完之后,就创建了一个podspec文件,他其中会包含很多内容,可以按照我之前介绍的进行编辑,没用的删掉。编辑完成之后使用验证命令验证一下

pod spec lint xxx.podspec --verbose

验证无误就可以进入下一步了。

向Spec Repo提交podspec

Spec Repo提交podspec需要完成两点一个是podspec必须通过验证无误,在一个就是删掉无用的注释(这个不是必须的,为了规范还是删掉吧)。 向我们的私有Spec Repo提交podspec只需要一个命令。

$ pod repo push DemoSpecs  DSButton.podspec  #前面是本地Repo名字 后面是podspec名字

完成之后这个组件库就添加到我们的私有Spec Repo中了,可以进入到~/.cocoapods/repos/DemoSpecs目录下查看

├── LICENSE
    ├── DemoSpecs
    │   └── 0.2.0
    │       └── DSButton.podspec
    └── README.md

再去看我们的Spec Repo远端仓库,也有了一次提交,这个podspec也已经被push上去了。

至此,我们的这个组件库就已经制作添加完成了,使用pod search命令就可以查到我们自己的库了

-> DSButton (0.2.0)
   A short description of DSButton.
   pod 'DSButton', '~> 0.2.0'
   - Homepage: https://dev.tencent.com/u/iDog/p/DSButton
   - Source:   https://git.dev.tencent.com/iDog/DSButton.git
   - Versions: 0.2.0 [DemoSpecs repo]

这里说的是添加到私有的Repo,如果要添加到Cocoapods的官方库了,可以使用trunk工具,具体可以查看官方文档

使用制作好的Pod

在完成这一系列步骤之后,我们就可以在正式项目中使用这个私有的Pod了只需要在项目的Podfile里增加以下一行代码即可

$ pod 'DSButton', '~> 0.2.0'

然后执行pod update,更新库依赖,然后打开项目可以看到,我们自己的库文件已经出现在Pods子项目中的Pods子目录下了,而不再是Development Pods

更新维护podspec

说一下制作好的podspec文件后续的更新维护工作,比如如何添加新的版本,如何删除Pod

我已经制作好了DSButton的0.2.0版本,现在我对他进行升级工作,这次我添加了更多的模块到DSButton之中。这里又尝试了一下subspec功能,给DSButton创建了多个子分支。

具体做法是先将源文件添加到Pod/Classes中,然后按照不同的模块对文件目录进行整理,因为我有四个模块,所以在Pod/Classes下有创建了四个子目录,完成之后继续编辑之前的DSButton.podspec,这次增加了subspec特性。

# Be sure to run `pod lib lint DSButton.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             = 'DSButton'
  s.version          = '0.3.0'
  s.summary          = 'A short description of DSButton.'

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!

  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC

  s.homepage         = 'https://dev.tencent.com/u/iDog/p/DSButton'
  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'liuhuan' => 'idoghuan@163.com' }
  s.source           = { :git => 'https://git.dev.tencent.com/iDog/DSButton.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 = 'DSButton/Classes/**/*'

  # s.resource_bundles = {
  #   'DSButton' => ['DSButton/Assets/*.png']
  # }

  s.subspec 'DSButtton' do |ds|
      ds.source_files = 'DSButton/Classes/DSButtton/*.{h,m}'
      ds.public_header_files = 'DSButton/Classes/DSButtton/*.h'
  end

  s.subspec 'DSButttonA' do |a|
      a.source_files = 'DSButton/Classes/DSButttonA/*.{h,m}'
      a.public_header_files = 'DSButton/Classes/DSButttonA/*.h'
      a.dependency 'AFNetworking', '~> 2.3'
  end

  s.subspec 'DSButttonB' do |b|
      b.source_files = 'DSButton/Classes/DSButttonB/*.{h,m}'
      b.public_header_files = 'DSButton/Classes/DSButttonB/*.h'
      b.dependency 'OpenUDID'
  end

  s.subspec 'DSButttonC' do |c|
      c.source_files = 'DSButton/Classes/DSButttonC/*.{h,m}'
      c.public_header_files = 'DSButton/Classes/DSButttonC/*.h'
      #c.dependency 'DSButtton/DSButtton'
  end


  # s.public_header_files = 'Pod/Classes/**/*.h'
  s.frameworks = 'UIKit'
  # s.dependency 'AFNetworking', '~> 2.3'
end

因为我们创建了subspec所以项目整体的依赖dependency,源文件source_files,头文件public_header_files,资源文件resource等都移动到了各自的subspec中。

每个subspec之间也可以有相互的依赖关系,比如DSButttonC就依赖于DSButtton依赖失败

编辑完成之后,在测试项目里pod update一下,几个子项目都被加进项目工程了,写代码验证无误之后,就可以将这个工程push到远端仓库,并打上新的tag->0.3.0

最后再次使用pod lib lint验证编辑好的podsepc文件,没有自身的WARNING或者ERROR之后,就可以再次提交到Spec Repo中了,命令跟之前是一样的。

pod repo push DemoSpecs DSButton.podspec --verbose --allow-warnings

之后再次到~/.cocoapods/repos/DemoSpecs目录下查看

.
├── DSButton
│   ├── 0.2.0
│   │   └── DSButton.podspec
│   └── 0.3.0
│       └── DSButton.podspec
├── LICENSE
└── README.md

3 directories, 4 files

已经有两个版本了,使用pod search查找得到的结果为

-> DSButton (0.3.0)
   A short description of DSButton.
   pod 'DSButton', '~> 0.3.0'
   - Homepage: https://dev.tencent.com/u/iDog/p/DSButton
   - Source:   https://git.dev.tencent.com/iDog/DSButton.git
   - Versions: 0.3.0, 0.2.0 [DemoSpecs repo]
   - Subspecs:
     - DSButton/DSButtton (0.3.0)
     - DSButton/DSButttonA (0.3.0)
     - DSButton/DSButttonB (0.3.0)
     - DSButton/DSButttonC (0.3.0)

完成这些之后,在实际项目中我们就可以选择使用整个组件库或者是组件库的某一个部分了,对应的Podfile中添加的内容为

source 'https://github.com/CocoaPods/Specs.git'  # 官方库
source 'https://git.dev.tencent.com/iDog/DemoSepcs.git'   # 私有库

platform :ios, '8.0'

target "TestButton" do

pod 'DSButton/DSButttonA' #导入小组件
pod 'DSButton/DSButttonB' #导入小组件
pod 'DSButton' #导入全部组件
end

删除和添加

删除一个私有Spec Repo,只需要执行一条命令即可

$ pod repo remove DSButton

这样这个Spec Repo就在本地删除了,我们还可以通过

$ pod repo add DemoSepcs git@git.dev.tencent.com:iDog/DemoSepcs.git

再把它给加回来。

如果我们要删除私有Spec Repo下的某一个podspec怎么操作呢,此时无需借助Cocoapods,只需要cd~/.cocoapods/repos/DemoSpecs目录下,删掉库目录

$ rm -Rf DemoSpecs

然后在将Git的变动push到远端仓库即可。

$ git add  .
$ git ci -m "remove unuseful pods"
$ git push origin master

常见问题

error: unable to find utility “simctl”, not a developer tool or in PATH

解决方案就是去xcode设置里面,将Command line Tools设置一下,在Xcode>preferences>Locations里面,设置之后再运行终端即可

The spec did not pass validation, due to 1 warning (but you can use --allow-warnings to ignore it).

$ pod spec lint xxx.podspec --verbose  --allow-warnings

如果.podspec文件验证通过还是报如下的错误: specification does not validate

$ pod repo push DemoSpecs DSButton.podspec --verbose --allow-warnings

pod search时报错: Unable to find a pod with name, author, summary, or description matching

$ rm ~/Library/Caches/CocoaPods/search_index.json
$ pod search DSButton

参考博客