第一部分: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 文件,以
AFNetworking
,JSONKit
,Reachability
为例,编辑后的内容如下:Podfile更多的语法。
编辑完成保存并退出,终端进入到该项目目录下,使用下述命令完成三方库的安装。
进入到项目目录。
$ cd desktop/cocoapodsDemo
执行 CocoaPods 安装命令。
$ pod install
出现以下提示,表示成功安装。
注意,安装完成之后,当前项目
Project
不能正常运行了,我们需要使用 CocoaPods 为我们创建的 workspace
来运行项目。我们重新打开项目目录。对比最初的目录结构,这里已经多出了四个文件,其中:
-
Podfile
是三方库的配置文件 -
xxx.xcworkspace
是管理我们项目和所有依赖库的workspace
-
Podfile.lock
是执行安装后的信息 -
Pods
存放了三方库文件以及系统依赖库
我们打开这里的 xxx.xcworkspace
。项目结构如下:
以后你就可以通过
xxx.xcworkspace
来运行你的项目了,其他关于三方库的部分都交给 CocoaPods 来管理吧。
Cocoapods 帮你做了什么呢?
打开 cocoapodsDemo
项目的 Frameworks
,你可以找到一个 libPods-xxx.a
文件。
CocoaPods 通过文件
Podfile
中的三方库信息下载你所需要的库,并将这些库编译成一个 .a
的静态库链接到你的项目,除此之外,CocoaPods 帮你完成依赖库的管理,它能够去除三方库的依赖库的重复库,在你删除不需要的三方库是,同样自动删除多余的依赖库。
CocoaPods 将所有的第三方库以 target
的方式组成一个名为Pods的工程里,我们的工程和第三方库所在的工程 Pods 由一个新生成的 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
中就是三方库的更详细的信息,例如作者,当前版本,介绍,目录结构,依赖库等等。
由于 CocoaPods 在时刻更新,所以你的缓存并非最新,所以你可能需要更新本地的
repos
。使用下面命令来更新。
$ pod repo update
该命令会同步所有库的最新本版信息,如果你只想关心指定的库,你可以使用下面命令。
pod repo update ~/.cocoapods/repos/***/库名
如果指定更新依旧无法更新,但是你急需使用最新的库,你可以去官网git手动去下载对应的库,也可以从有的同事小伙伴那边直接复制。
- 更多相关的命令
第二部分:让自己的库支持 CocoaPods
需求
我们想要将自己开发的工具组件给他人使用,通常将共享文件封装成 framework,让别人手动导入到自己的项目中去,这也是开放平台的通常做法。除此之外,我们还可以通过 Cocoapods 来分享自己的工具组件,就像 AFNetworking
、SDWebImage
一样。
必要条件
- 项目代码库(如 git、码云等)并发布了release。
-
podspec
配置文件 -
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
确认之后,系统会为你自动配置项目,创建好的项目如下:
- Example 工程
项目文件目录中存在一个名为 Example
的工程,这个工程是你选择 Would you like to include a demo application with your library?
中选择 Yes
时为你添加的,这个还是很有用的,在你开发过程中可以通过它来集成测试组件功能的正确性、完整性。 我们先打开这个 Example
来看下:
这个 Example
已经为你的组件创建了索引文件 podspec
,并且集成了该组件。我们来看下 Example
的 Podfile
的内容:
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
创建对应的仓库。
在最初创建组件时,系统已经帮我们创建好了本地 Git 仓库,进入到项目中,显示出隐藏文件夹就可以看到(command+shift+. 显隐):
如果没有,你可以使用命令 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.0
的 tag
:
$ git tag 0.1.0 // 打 tag
$ git push --tags // 推送到远程
打 tag
默认在当前分支上,因为这里只有 master
,所以不用切换分支,如果后期有其他分支,注意别弄错了。
刷新页面,项目的 release
选项中会出现刚刚打的版本。
你也可以直接在页面的 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
。