iOS 必备工具 - CocoaPods

第一部分:CocoaPods 的使用


需求

在 iOS 开发过程中,当你需要使用必要的三方库时,是否被各种库搞得筋疲力尽?
当你需要使用三方库时,你不仅需要手动下载库,还需要为该库添加可能依赖的系统库;在库需要更新时,你依然需要重复上述操作,并且当你想要删除一些不再需要的三方库时,你还需要连同依赖的系统一并删除,删除的过程中特别注意其他库所依赖的部分。总之,手动管理库的过程相当的繁琐而又容易出错。那么,有没有相关的工具帮我们完成这些繁琐的是呢?这就要提到 iOS 程序员需要掌握的必备工具 - CoCoaPods。无论你是新手还是老手,这工具都是接下来经常打交道的,所以,这里推荐新手们快速的掌握该工具的使用,因为它能帮你省去相当多的麻烦。

CocoaPods简介

CocoaPods 是库管理工具,iOS 开发过程中会遇到两个头疼的问题 -- 依赖库的重复性和三方库的更新,使用 CocoaPods 管理工具帮你自动完成上述工作,而你所需要做的事情就是编写一份文件,来指定需要的三方库以及版本等信息,然后一句更新命令即可。另外绝大部分有名的开源库,都支持了 CocoaPods,这也让 iOS 开发人员避不开使用 CocoPods。当然你也可以发布自己的库到 CocoaPods 上,分享你的工具。

想要了解更多的信息,可以查阅 官方指导文档

安装 CocoaPods

安装 CocoaPods 需要 Ruby 环境,幸运的是,OS X 系统默认安装了 Ruby 环境,你可以使用下面的命令来查看当前的 Ruby 版本。

$ ruby -v 

CocoaPods 要求 Ruby 一定版本的,一般大于 2.2.0 即可,如果还不行,选择更高的版本即可。如果你的 Mac 达到了要求,那么使用下面的命令安装即可。

$ gem install cocoapods

如果你的账号在电脑上没有权限,使用下面命令,并输入电脑的密码。

$ sudo gem install cocoapods

如果顺利安装完成,执行以下命令来进行初始化,它会下载服务器中所有的框架信息,缓存到电脑本地。过程可能需要较长时间。

$ pod setup

如果顺利,你就可以跳到使用 CocoaPods 使用步骤。

安装过程中可能出现的问题

  • 执行 gem install cocoapods 没反应

因为 Ruby 默认的来源是 https://rubygems.org/ , 国内访问时如果没有梯子时会非常困难,可以将源替换为国内的镜像服务器。

执行下面命令查看当前使用的源。

$ gem sources -l

将源替换为国内镜像服务器的源。先删除再添加。

$ gem sources --remove https://rubygems.org/
$ gem sources -a https://gems.ruby-china.com/
  • Ruby 版本过低

当出现提示 active support requires Ruby version >=2.2.0 字样时,你需要将电脑上 Ruby 环境的版本升级到 2.2 以上。关于升级 Ruby,你可以使用 RVM(Ruby Version Manager,Ruby版本管理器)。

$ curl -L get.rvm.io | bash -s stable
$ source ~/.bashrc
$ source ~/.bash_profile

升级 Ruby

列出当前可以使用的各个版本信息。

$ rvm list known rubies

安装对应的版本

$ rvm install x.x.x

更多的问题,请自行搜索网络最新文章。

使用 CocoaPods 管理三方库

我们先新建一个演示工程:cocoapodsDemo。

在终端使用 cd 命令进入到该项目下,执行下面命令来创建三方库的配置文件 Podfile。你也可以手动创建,但是名称必须为 Podfile

$ pod init

使用 vim Podfile 命令进入该文件,或者直接在项目结构中查看。

项目结构中的Podfile文件

编辑 Podfile 文件,以 AFNetworkingJSONKitReachability为例,编辑后的内容如下:
编辑Podfile文件

Podfile更多的语法

编辑完成保存并退出,终端进入到该项目目录下,使用下述命令完成三方库的安装。

进入到项目目录。

 $ cd desktop/cocoapodsDemo

执行 CocoaPods 安装命令。

$ pod install

出现以下提示,表示成功安装。

成功安装三方库

注意,安装完成之后,当前项目 Project 不能正常运行了,我们需要使用 CocoaPods 为我们创建的 workspace 来运行项目。我们重新打开项目目录。
安装后的目录

对比最初的目录结构,这里已经多出了四个文件,其中:

  • Podfile 是三方库的配置文件
  • xxx.xcworkspace 是管理我们项目和所有依赖库的 workspace
  • Podfile.lock 是执行安装后的信息
  • Pods 存放了三方库文件以及系统依赖库

我们打开这里的 xxx.xcworkspace。项目结构如下:

workspace的目录结构

以后你就可以通过 xxx.xcworkspace 来运行你的项目了,其他关于三方库的部分都交给 CocoaPods 来管理吧。

Cocoapods 帮你做了什么呢?

打开 cocoapodsDemo 项目的 Frameworks ,你可以找到一个 libPods-xxx.a 文件。

编译的 .a 文件

CocoaPods 通过文件 Podfile 中的三方库信息下载你所需要的库,并将这些库编译成一个 .a 的静态库链接到你的项目,除此之外,CocoaPods 帮你完成依赖库的管理,它能够去除三方库的依赖库的重复库,在你删除不需要的三方库是,同样自动删除多余的依赖库。

CocoaPods 将所有的第三方库以 target 的方式组成一个名为Pods的工程里,我们的工程和第三方库所在的工程 Pods 由一个新生成的 workspace 管理。

workspace的组成

更多常用的 pod 命令

  • 新增或删除三方库

Podfile 新增或删除指定三方库的配置信息。重新执行下面命令。

$ pod install
  • 搜索三方库的信息

你可以使用 search 搜索一些三方库的信息,你无需全匹配,模糊搜索即可。

$ pod search AFNetworking
$ pod search SDWeb

注:search 命令只会使用本地所创建的索引进行搜索,因此,你搜索到的最新库可能并不是最新版本。

  • 更新框架 / 更新本地索引文件

当你的 Podfile 文件中没有为三方库指定特定版本,但后续又想将三方库更新到最新,你可以使用 update 命令来更新本地索引并完成类似 install 的安装效果。

# 同步远程最新信息
$ pod update
# 只根据本地缓存更新到最新的版本
$ pod update --no-repo-update
  • 更新指定库

有时候你可能并不需要更新所有的三方库,这种情况经常出现,例如你的 Podfile 文件中只有部分库指定了版本,你这时候使用 update 命令,默认是会将所有未指定的三方库都进行了更新,如果三方库出现 api 更新或替换,你的项目将出现错误问题。又或者你的网络不稳定,你只想更新一个或指定的几个库,你需要下面的命令来进行指定库的更新。

$ pod update 三方库  --verbose --no-repo-update
  • 本地索引库在哪里?

通过 shift + command + G 打开前往文件夹,输入

~/.cocoapods/repos/

找到 master/specs 文件夹,该文件夹下缓存了 CocoaPods 的所有的三方库信息,有三层文件夹,分别是 0-f ,随便点开某个三方库的索引文件,你可以在这里看到该三方库的历史版本。

某个库的索引文件

其中,文件 xxxx.podspec.json 中就是三方库的更详细的信息,例如作者,当前版本,介绍,目录结构,依赖库等等。

xxx.podspec.json

由于 CocoaPods 在时刻更新,所以你的缓存并非最新,所以你可能需要更新本地的 repos 。使用下面命令来更新。

$ pod repo update

该命令会同步所有库的最新本版信息,如果你只想关心指定的库,你可以使用下面命令。

pod repo update ~/.cocoapods/repos/***/库名

如果指定更新依旧无法更新,但是你急需使用最新的库,你可以去官网git手动去下载对应的库,也可以从有的同事小伙伴那边直接复制。

  • 更多相关的命令

cocoapods的命令资料


第二部分:让自己的库支持 CocoaPods

需求

我们想要将自己开发的工具组件给他人使用,通常将共享文件封装成 framework,让别人手动导入到自己的项目中去,这也是开放平台的通常做法。除此之外,我们还可以通过 Cocoapods 来分享自己的工具组件,就像 AFNetworkingSDWebImage 一样。

必要条件

  1. 项目代码库(如 git、码云等)并发布了release。
  2. podspec 配置文件
  3. cocoapods 账号

如果你已经有了 Git 仓库,你只需要在项目中创建并配置你的项目信息即可,文件名为 yourProjectName.podspec 。该文件语法可以看 官网指导

其他部分你可以参考这篇文章 iOS 让自己的框架支持cocoapods

接下来的篇幅,主要介绍的是从无到有的过程,这部分的内容节选自 iOS 组件化 中的内容。关于组件化你可以通过上面的链接来了解更多。

我们来创建一个关于网络请求的功能组件 LLNetworking

  • 拉取模版

我们将创建在桌面上的一个名为 Demo 文件夹。通过终端进入到该文件夹下,然后输入命令:

$ pod lib create LLNetworking

这个命令会为了拉取 Pod 的 基础模板。拉取之后,还会通过询问的形式为你配置一些东西:

// 作用的平台
What platform do you want to use?? [ iOS / macOS ]
 > iOS

// 语言环境
What language do you want to use?? [ Swift / ObjC ]
 > ObjC

// 是否需要一个 demo 用来测试组件
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 ]
 > Yes

// 组件中,文件的前缀
What is your class prefix?
 > LL

确认之后,系统会为你自动配置项目,创建好的项目如下:

LLNetworking
  • Example 工程

项目文件目录中存在一个名为 Example 的工程,这个工程是你选择 Would you like to include a demo application with your library? 中选择 Yes 时为你添加的,这个还是很有用的,在你开发过程中可以通过它来集成测试组件功能的正确性、完整性。 我们先打开这个 Example 来看下:

项目结构

这个 Example 已经为你的组件创建了索引文件 podspec,并且集成了该组件。我们来看下 ExamplePodfile 的内容:

use_frameworks!

platform :ios, '8.0'

target 'LLNetworking_Example' do
  pod 'LLNetworking', :path => '../'

  target 'LLNetworking_Tests' do
    inherit! :search_paths

    pod 'FBSnapshotTestCase'
  end
end

其中为你集成了一个测试用例 pod 'FBSnapshotTestCase',目前可以忽略。

我们可以看到: pod 'LLNetworking', :path => '../' 这一句,path 路径指向了本地路径,对应 LLNetworking 主目录下:

组件目录

这个文件夹下,一个存放你的各种类文件,一个存放图片资源等。

  • podspec 文件

在你回答之前问题之后,pod 为你自动创建了该文件,并执行了 pod install 命令,该命令会找到组件的索引文件(也在本地) LLNetworking.podspec

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

# 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://github.com/LOLITA0164/LLNetworking'
  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'LOLITA0164' => '476512340@qq.com' }
  s.source           = { :git => 'https://github.com/LOLITA0164/LLNetworking.git', :tag => s.version.to_s }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.ios.deployment_target = '8.0'

  s.source_files = 'LLNetworking/Classes/**/*'
  
  # s.resource_bundles = {
  #   'LLNetworking' => ['LLNetworking/Assets/*.png']
  # }

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

该文件为你的组件自动配置了一些基本的信息,因为我之前使用过 trunk 登陆过,所以这里有的的账号信息。当然这些信息是需要你根据情况修改的,更多的配置你可以搜索相关文档。

注意:这里的 Git 地址目前是找不到的,后期需要自己关联。

设置共享文件

podspec 文件中 s.source_files = 'LLNetworking/Classes/**/*' 指代共享的资源路径,我们需要将共享的文件放到这里来。

共享文件夹

我们打开组件的目录查看,可以看到这里已经有了名为 ReplaceMe 的文件了,这暗示你用共享文件替换它。

podspec 文件中还有一个被注释掉的:

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

这个目录中存放一些图片等资源,当你需要的时候可以开启来。

我们来创建一个 LLNetworking 类:

@interface LLNetworking : NSObject
-(NSString*)getSomething;
@end

@implementation LLNetworking
-(NSString *)getSomething{
    return @"test method.";
}
@end

将其移动到组件的共享目录下并删除掉空文件ReplaceMe

设置共享文件

这样,我们就设置好了共享的内容,即组件开发好了。接下来,我们使用 Example 工程来使用这个组件的内容。

终端进入 Example 工程目录下,执行 pod install 命令来安装组件。

安装新的组件

我们发现,Example 项目中 Pods/Development Pods/LLNetworking 下,多出来最新添加的文件。

使用组件

我们安装好组件之后来使用一下组件的功能,就像使用三方库那样:

#import <LLNetworking/LLNetworking.h>

-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    LLNetworking * networking = LLNetworking.new;
    NSLog(@"%@",networking.getSomething);
}

控制台输出:

2019-11-08 17:14:47.455341+0800 LLNetworking_Example[7038:1682304] test method.

这表示功能正常。

在组件开发过程中,使用 pod 'LLNetworking', :path => '../' 将路径指向本地是很有必要的,方便测试你的组件配置是否正确,功能是否完善,相比推到远程、发布再集成,这方便太多了。

三方依赖库

有时候,我们的组件还依赖其他的组件,又或者是三方库。我们通过 s.dependency 字段去设置,多个库可以分开写多次。

Podfiles 模版里最后一条已经为我们添加好了,所依赖的是 AFNetworking ,正好是我们网络请求组件所依赖的,我们把它开启,重新 pod install

Analyzing dependencies
Fetching podspec for `LLNetworking` from `../`
Downloading dependencies
Installing AFNetworking (2.7.0)
……

我们发现,Example 自动拉取了组件 LLNetworking 所依赖的其他组件。CocoaPods 工具的另外一个优点就是,多个组件依赖同一个组件时,它会自动帮你检测安装,而不会重复导入。

本地组件和远程组件

我们发现 Example 工程的 Pods中,本地开发的组件和远程发布的组件被分别放在了不同的目录下。

有了 AFNetworking 之后,你就可以修改你的网络请求组件了:

#import <AFNetworking/AFNetworking.h>
@interface LLNetworking : NSObject
@property(strong,nonatomic)NSURLSessionDataTask *task;
- (NSURLSessionDataTask *)POSTWithURLString:(NSString *)URLString parameters:(id)parameters success:(void (^)(id responseObject))success failure:(void (^)(id error))failure;
@end

@implementation LLNetworking
- (NSURLSessionDataTask *)POSTWithURLString:(NSString *)URLString parameters:(id)parameters success:(void (^)(id responseObject))success failure:(void (^)(id error))failure{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    manager.requestSerializer.timeoutInterval = 20;
   _task =  [manager POST:URLString parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
       if (success) {
           success(@{@"status":@"success"});
       }
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure) {
            failure(@{@"status":@"failure"});
        }
    }];
    return _task;
}
@end

修改好之后,还不能直接在 Example 中使用,需要卸载组件再重新安装。注释掉 pod 'LLNetworking', :path => '../' 之后执行 pod install 即可完成卸载。

至此,你完成了组件的创建、文件共享、本地化测试使用和更新。但是,我们的组件毕竟是要服务于宿主工程的,如果仅仅只能是通过本地集成,那意义不大,我们要将其关联到远程服务器,推送到本地搭建的 GitLab,又或者是 GitHub码云Coding 等平台。

关联远程仓库

在模版 podspec 文件中,已经帮我们指定了一个 GitHub 的仓库地址,

s.homepage         = 'https://github.com/LOLITA0164/LLNetworking'

你可以使用它或者进行修改它。我们这里选择使用它,先去 GitHub 创建对应的仓库。

创建演示demo

在最初创建组件时,系统已经帮我们创建好了本地 Git 仓库,进入到项目中,显示出隐藏文件夹就可以看到(command+shift+. 显隐):

本地git仓库

如果没有,你可以使用命令 git init 创建一个。现在,我们要将之前的修改进行提交(本地提交)。

git commit -am "第一次提交"  

然后我们要把本地的 Git 仓库和刚刚创建的远程仓库进行关联。如何关联呢?你在网站上创建项目后有了这样的提示:

关联远程仓库

这里有三种:创建一个新的仓库,推送一个已存在的仓库以及从其他仓库导入。我们这里使用第二种即可。

添加远程仓库:

git remote add origin https://github.com/LOLITA0164/LLNetworking.git

将本地内容推送到远程仓库:

git push -u origin master

可能会出现让你登陆验证,输入你的用户名和密码即可。出现以下信息即表示推送成功。

remote: Resolving deltas: 100% (49/49), done.
To https://github.com/LOLITA0164/LLNetworking.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

回到 GitHub 刷新一下即可看到你的提交记录。

上述是通过终端命令进行 git 操作,如果你并不熟悉 git 命令,你大可以使用便捷的可视化工具(上一章节有所提及),仅需简单的点击操作即可完成项目的管理。

tag 并发布到 Cocoapods

打标签

至此,我们已经成功的将本地仓库关联并推送到远程仓库,现在我们要发布一个可用的组件。

首先我们要给当前项目打一个 tag 版本号,在 podspec 中:

s.version          = '0.1.0'

指定的版本号是 0.1.0,那么我们就同样打个 0.1.0tag:

$ git tag 0.1.0  // 打 tag
$ git push --tags  // 推送到远程

tag 默认在当前分支上,因为这里只有 master,所以不用切换分支,如果后期有其他分支,注意别弄错了。

刷新页面,项目的 release 选项中会出现刚刚打的版本。

打tag

你也可以直接在页面的 release 下添加新的 tag,点击 release 可以看到编辑页面:

在线打包

发布到 CocoaPods

由于我们创建的项目以及标签的版本号都是沿用了 podspec 文件中的信息,因此可以直接验证 podspec 文件信息是否可以通过验证,如果需要调整,调整之后最好同样先验证:

pod spec lint

podspec 文件的版本号一定要和 tag 保持一致。

如果通过验证,那么你会看到类似下面的提示,绿色的 passed validation

验证通过

现在可以将 podspec 文件提交到 CocoaPods 上了:

首先要通过 trunk 注册生成一条会话:

// pod trunk register 邮箱 用户名 描述
pod trunk register 476512340@qq.com LOLITA0164 --description=组件化demo 

然后去邮箱进行验证,验证成功会出现下面页面:

验证通过

现在,就可以将 podspec 提交给 CocoaPods 了。这个文件将是别人搜索你的组件的索引。

pod trunk push LLNetworking.podspec --allow-warnings

上传完成之后,接可以通过 pod search LLNetworking 搜索到自己的组件了,如果搜索不到,删除本地的搜索文件,命令 :

rm ~/Library/Caches/CocoaPods/search_index.json

重新 search 产生新的搜索文件。

发布新版本则需要打新的 tag,重新编辑 podspec 文件,然后再次提交给 CocoaPods

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容