CocoaPods 可以说是 iOS 开发应用最广泛的包管理工具,本篇文章主要介绍 CocoaPods 的第三方库是怎样从网络集成到我们本地的项目当中,也是制作私有库、开源库和 iOS 项目组件化的一个知识铺垫。
让我们从一张图片开始:
CocoaPods 工作流程 |
---|
远程索引库
远程索引库里存放的是各种框架的描述信息,这个库托管在 Github 上,地址如下:
https://github.com/CocoaPods/Specs
每个框架下有数个版本,每个版本有一个 json
格式的描述信息,如下:
{
"name": "CAIStatusBar",
"version": "0.0.1",
"summary": "A simple indicator",
"homepage": "https://github.com/apple5566/CAIStatusBar.git",
"license": "MIT",
"authors": {
"apple5566": "zaijiank110@sohu.com"
},
"platforms": {
"ios": "6.0"
},
"source": {
"git": "https://github.com/apple5566/CAIStatusBar.git",
"tag": "0.0.1"
},
"source_files": "CAIStatusBar/**/*.{h,m}",
"resources": "CAIStatusBar/CAIStatusBar.bundle",
"requires_arc": true
}
其中 git
字段表示该框架的托管地址,也就是上面时序图中的 远程框架库
。
本地索引库
在 install cocoapods
命令后,需要执行 pod setup
这个命令,pod setup
命令就是将远程索引库克隆到本地来,本地索引库的路径如下:
~/.cocoapods/repos/master
本地索引库和远程索引库的目录一致,结构如下:
本地索引库 |
---|
本地索引文件
当执行 pod search
命令时,如果本地索引文件不存在,会创建这个文件。
tianziyaodeMacBook-Air:~ Tian$ pod search afn
Creating search index for spec repo 'master'..
如果这个文件存在,则会在此文件中进行索引,确认所需要的框架是否存在,本地索引文件的路径如下:
~/资源库/Caches/CocoaPods
制作 CocoaPods 库
上面的流程清楚以后,制作 CocoaPods 库相信应该不会太难了,大致分为以下几步:
- 托管框架源码到 Git;
- 创建框架描述信息;
- 上传框架描述信息到
https://github.com/CocoaPods/Specs
; - 命令行
pod setup
, 创建本地索引库; - 命令行
pod install
,将框架集成到项目中;
现在开始动手吧!首先在桌面新建一个 testLib
目录,在该目录下新建一个 Classes
目录,用来存放框架源码,然后将 testLib
托管到 Git。
你可以给 Classes 目录任意的命名,Classes 只是一种约定俗称的命名。
pod spec
pod spec
命令用于创建框架的描述信息文件,文档如下:
https://guides.cocoapods.org/syntax/podspec.html
现在在 testLib
目录下执行:
pod spec create testLib
目录下会创建一个 testLib.podspec
文件,然后编辑这个文件,主要有以下几个字段:
- version:这个 spec 映射的版本,保证 Git 的
releases
与此对应; - homepage:项目主页;
- source:框架源代码的托管地址;
- tag:与 version 对应;
- source_files:框架源代码的目录、文件、文件类型等规则;
CocoaPods 公开库
根据上面的步骤,现在你需要将生成的 testLib.podspec
文件上传到远程索引库,在此之前,你需要注册一个 Trunk 账号,文档如下:
https://guides.cocoapods.org/making/getting-setup-with-trunk.html
现在执行下面的命令,记得修改邮箱昵称描述等:
pod trunk register ziyao.tian@gmail.com 'Tian' --description='macbook air'
你的邮箱会收到一封邮件,打开邮件里面的链接,会有类似 you can back termainal
的提示,现在回到终端。
pod lib lint
检查 testLib.podspec
的合法性,根据错误提示修复问题,当显示 passed validation
后,执行下面的命令:
pod trunk push testLib.podspec
提示信息如下:
Updating spec repo `master`
--------------------------------------------------------------------------------
🎉 Congrats
🚀 testLib (0.0.7) successfully published
📅 October 17th, 00:38
🌎 https://cocoapods.org/pods/testLib
👍 Tell your friends!
--------------------------------------------------------------------------------
此时你的 testLib.podspec
就会 pull request
到远程索引库,CocoaPods 官方审核通过后,就可以出现在远程索引库中,当远程索引库收录后:
pod setup
这时你的本地索引库,会新加入 testLib.podspec
这条记录,但是本地索引文件还未更新,因此删除掉以下路径的本地索引文件:
~/资源库/Caches/CocoaPods/search_index.json
执行 pod search testLib
命令,当 search_index.json
文件重建完毕后,就可以在使用这个远程框架库了。
CocoaPods 私有库
有了公开库,当然也就有私有库,私有库主要分为远程和本地两种,什么时候会用到私用库呢?也就是需要将源码封装成库,但又不希望将源码公开,一般的使用场景是公司内部的组件化开发。
本地私有库
本地私有库就是创建一个仓库,将其存储在本地,在本地的其他工程中直接使用。首先在桌面新建一个库,路径如下:
LocalLib/NetWork/Classes/Test.swift
接着创建一个壳工程,现在你的目标是使用 pod
的方式,将 NetWork
这个库集成到壳工程中。
创建本地 GIt 仓库
将 NetWork
加入到 Git,命令如下:
git init
git add.
git commit -m 'x'
创建库描述文件
和公开库一样,我们需要先创建一个 spec
文件,命令如下:
pod spec create LocalLib
编辑 NetWork.podspec
文件,修改成下面这样:
Pod::Spec.new do |s|
s.name = "NetWork"
s.version = "0.0.1"
s.summary = "A short description of NetWork."
s.description = "A short description of NetWork.xxxxxxxxxxxxxxxxxx"
s.homepage = "http://EXAMPLE/NetWork"
s.license = "MIT"
s.author = { "tianziyao" => "ziyao.tian@gmail.com" }
s.source = { :git => "", :tag => "#{s.version}" }
s.source_files = "Classes", "Classes/**/*.{h,m,swift}"
end
现在你的本地库已经准备完毕了,下面就可以使用这个库了。
导入本地私有库
现在进入到壳工程目录下,执行命令:
pod init
编辑 Podfile
文件,如下:
target 'Test' do
use_frameworks!
pod 'NetWork', :path => '../NetWork'
target 'TestTests' do
inherit! :search_paths
end
target 'TestUITests' do
inherit! :search_paths
end
end
这里有一个 path
关键字,它表示在 pod install
执行时,在指定的路径下寻找 NetWork.podspec
文件。
下面执行 pod install
命令,提示信息如下:
Analyzing dependencies
Fetching podspec for `NetWork` from `../NetWork`
Downloading dependencies
Installing NetWork (0.0.1)
Generating Pods project
Integrating client project
现在 NetWork
这个库就集成到了壳工程中。
与使用远程库不同,本地库的源文件会在 Development Pods
这个目录下,而不是 Pods
目录,顺便一提,CocoaPods 的库开发,一般也是这样搭建环境的,开发完成后再修改 spec
文件,将其 pull request
到远程索引库。
CocoaPods 模板库
本地私有库这个方式还存在以下问题:
- 需要手动创建
podspec
文件; - 无法单独测试,需要依托于壳工程运行;
假设我们有一个基础组件,里面全部是扩展文件,无法单独运行,如果依托壳工程运行,只有这一个组件,那么这个壳工程实际跟测试工程是一样的,但壳工程内有多个组件呢?
我们在壳工程中进行测试的话,不但要对其他的组件进行编译,而且自己负责的组件也可能会收到其他组件的影响,这样也就失去了组件化开发的本意,那么怎么优化呢?
单独测试
首先在 LocalLib/NetWork/
路径下创建一个测试工程 Example
,然后将 Classes
拖到这个测试工程中,这里需要注意的是,Example
和 Classes
是引用关系,不要 Copy。
简单粗暴的拖拽,现在 Example
工程就可以使用 NetWork
库了。
另外一种方式是将 NetWork
通过 CocoaPods 安装在 Example
中,和安装在壳工程一样。
看到这里,是不是感觉很烦?就是想做个测试而已,还要拖来拖去,那么繁琐。
不要着急下面来介绍一种更快捷高效的方式,执行下面的命令:
pod lib create BaseMoudle
////////////////////////////////////////////////////////////////////////
What language do you want to use?? [ Swift / ObjC ]
> Swift
Would you like to include a demo application with your library? [ Yes / No ]
> Yes
Which testing frameworks will you use? [ Quick / None ]
> None
Would you like to do view based testing? [ Yes / No ]
> Yes
现在我们就有了一个 CocoaPods 的模板工程,它的结构是这样的:
.
├── BaseMoudle
│ ├── Assets
│ └── Classes
│ └── ReplaceMe.swift
├── BaseMoudle.podspec
├── Example
│ ├── BaseMoudle
│ ├── BaseMoudle.xcodeproj
│ ├── BaseMoudle.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
看吧,把源码拖到 ReplaceMe.swift
的同级目录,执行 pod install
,就完成了本地私有库和其测试工程。
这一步可能会有 Swift 语言版本的问题,保持测试工程和私有库源码语言版本一致就可以。
远程私有库
远程私有库工作流程 |
---|
现在使用 pod lib create
就可以方便的生成一个本地私有库了,但是本地私有库有一定的局限性,例如:
- 需要在
Podfile
文件中主动指明路径; - 版本升级不容易维护;
- 多人开发时,不方便进行合作;
远程私有库就可以方便的解决以上的问题,制作远程私有库分为以下几个步骤:
- 创建私有 Git 远程仓库;
- 创建私有 CocoaPods 远程索引库;
- 创建 Pod 所需要的项目工程文件,并上传到 Git 远程私有库;
- 验证
podspec
描述文件; - 向私有 CocoaPods 远程索引库提交
podspec
描述文件; - 使用 Pod 库;
Git 仓库的创建在此就不在赘述了,本文中我使用码市做示例,私有 CocoaPods 远程索引库实际上也是一个 Git 仓库,现在我们有两个私有库,一个用来存放 Pod 库的源码,一个用来存放 Pod 库的描述文件。
SSH 授权
添加私有索引库需要使用 SSH 授权,也是和 Git 仓库一样的,了解的同学可以跳过这一步骤,首先创建公钥:
ssh-keygen
然后找到下面的文件:
~/.ssh/id_rsa.pub
里面存放的字符就是公钥了,然后将公钥添加码市,链接如下:
https://coding.net/user/account/setting/keys
添加私有远程索引库
现在执行 pod repo
,可以看到下面的信息:
master
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/Tian/.cocoapods/repos/master
现在我们只有一个 CocoaPods 远程索引库,也是官方的索引库,下面执行:
pod repo add TZYSpecs git@git.coding.net:tianziyao/TZYSpecs.git
此时我们的 CocoaPods 远程索引库就安装好了,到下面的路径去看一下:
~/.cocoapods/repos
上传源码到 Git
还记得 pod lib create
命令吗?前面我们使用它来制作了本地私有库,现在它又排上用场了,执行:
pod lib create BaseComponent
源码拖到 ReplaceMe.swift
的同级目录,它现在看起来应该是这个样子:
.
├── BaseComponent
│ ├── Assets
│ └── Classes
│ ├── Extension
│ │ ├── Array+Safe.swift
│ │ ├── CALayer+PauseAimate.swift
│ │ ├── UIImage+.swift
│ │ └── UIView+Property.swift
├── BaseComponent.podspec
├── Example
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
执行 pod install
,就完成了本地私有库和其测试工程,通过测试之后,我们就可以把这个本地私有库制作成远程私有库了。
首先修改 BaseComponent.podspec
文件:
Pod::Spec.new do |s|
s.name = 'BaseComponent'
s.version = '0.1.0'
s.summary = '基础组价'
s.description = '包括基本配置,常量,扩展,工具类等'
s.homepage = 'https://coding.net/u/tianziyao/p/BaseComponent'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'tianziyao' => 'ziyao.tian@gmail.com' }
s.source = { :git => 'https://git.coding.net/tianziyao/BaseComponent.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'BaseComponent/Classes/**/*'
end
然后使用质量检查工具验证一下,保证在 BaseComponent.podspec
路径下,执行:
pod lib lint
如果你使用 Swift,会得到一个提示:
-> BaseComponent (0.1.0)
- WARN | [iOS] swift_version: The validator for Swift projects uses Swift 3.0 by default, if you are using a different version of swift you can use a `.swift-version` file to set the version for your Pod. For example to use Swift 2.3, run:
`echo "2.3" > .swift-version`
[!] BaseComponent 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.
根据提示修复就好了,在这里你可能会遇到很多 Swift 语言版本的问题,善用搜索引擎吧,通过检验以后提示如下:
-> BaseComponent (0.1.0)
BaseComponent passed validation.
下面执行:
git add .
git commit -m 'x'
然后和远程仓库进行关联:
git remote add origin https://git.coding.net/tianziyao/BaseComponent.git
git pull origin master
git push origin master
上传 Spec 到远程索引库
首先执行下面的命令:
pod spec lint
提示如下:
-> BaseComponent (0.1.0)
- ERROR | [iOS] unknown: Encountered an unknown error ([!] /usr/local/bin/git clone https://git.coding.net/tianziyao/BaseComponent.git /var/folders/2v/qkx5m4sx4dg86x4c82yfyjdc0000gn/T/d20171021-69604-1bekfgk --template= --single-branch --depth 1 --branch 0.1.0
Cloning into '/var/folders/2v/qkx5m4sx4dg86x4c82yfyjdc0000gn/T/d20171021-69604-1bekfgk'...
warning: Could not find remote branch 0.1.0 to clone.
fatal: Remote branch 0.1.0 not found in upstream origin
) during validation.
Analyzed 1 podspec.
[!] The spec did not pass validation, due to 1 error.
根据提示,我们需要先建立一个 Tag:
git tag '0.1.0'
git push --tags
pod spec lint
检验通过后,提示如下:
-> BaseComponent (0.1.0)
Analyzed 1 podspec.
BaseComponent.podspec passed validation.
然后将 podspec
文件推到远程私有索引库:
pod repo push TZYSpecs BaseComponent.podspec
现在看一下本地索引库中是否已经添加成功:
~/.cocoapods/repos
再看一看你的远程索引库中是否添加成功,现在搜索一下本地索引文件试试:
-> BaseComponent (0.1.0)
基础组价
pod 'BaseComponent', '~> 0.1.0'
- Homepage: https://coding.net/u/tianziyao/p/BaseComponent
- Source: https://git.coding.net/tianziyao/BaseComponent.git
- Versions: 0.1.0 [TZYSpecs repo]
现在我们可以找到自己的远程私有库了,下面将 Podfile
文件改成这样:
project 'Ting.xcodeproj'
source 'https://github.com/CocoaPods/Specs.git'
source 'git@git.coding.net:tianziyao/TZYSpecs.git'
target 'Ting' do
use_frameworks!
pod 'BaseComponent'
pod 'Alamofire'
target 'TingTests' do
inherit! :search_paths
end
target 'TingUITests' do
inherit! :search_paths
end
end
执行 pod install
,整个远程私有库的搭建和使用就完成了。
CocoaPods 库升级
我们使用远程私有库的目的就是为了版本升级和多人开发,那么远程私有库如何进行升级,升级后其他人又如何使用呢?现在我们给 BaseComponent
进行升级,给它再增加一些功能:
.
├── BaseComponent
│ ├── Assets
│ └── Classes
│ ├── Const
│ │ └── Const.swift
│ ├── Extension
│ │ ├── Array+Safe.swift
│ │ ├── CALayer+PauseAimate.swift
│ │ ├── UIImage+.swift
│ │ └── UIView+Property.swift
│ └── Tool
│ ├── AlertTool.swift
│ ├── CacheTool.swift
│ ├── DeviceMessage.swift
│ └── NoticeLocalTool.swift
├── BaseComponent.podspec
├── Example
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
在 BaseComponent
的测试工程中测试无误后,将 BaseComponent.podspec
的 version
修改一下:
Pod::Spec.new do |s|
s.name = 'BaseComponent'
s.version = '0.2.0'
s.summary = '基础组价'
s.description = '包括基本配置,常量,扩展,工具类等'
s.homepage = 'https://coding.net/u/tianziyao/p/BaseComponent'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'tianziyao' => 'ziyao.tian@gmail.com' }
s.source = { :git => 'https://git.coding.net/tianziyao/BaseComponent.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'BaseComponent/Classes/**/*'
end
现在检查一下私有库是否有错误:
pod lib lint
检查通过后就可以将 BaseComponent
的 0.2.0
版本推到远程私有库中,同时建立 0.2.0
的 Tag。
然后检查一下 spec
文件:
pod spec lint
检查通过后,执行:
pod repo push TZYSpecs BaseComponent.podspec
远程私有库和远程私有索引库全部更新完毕,现在我们回到使用者的视角,这个库可以使用了吗?还不行。
因为本地的索引文件还没有更新,这个源还找不到,现在进入壳工程,执行:
pod update --no-repo-update
pod install
BaseComponent
的 0.2.0
版本就乖乖的进入了壳工程。
CocoaPods 库依赖
在上面的壳工程中,我们引入了 Alamofire
这个框架,但是如果用着用着突然觉得不爽了,要换框架,这时 Alamofire
的引用在工程中已经无处不再了,这样换的话是不是很痛苦?
所以我们一般在开发中都会封装网络请求,做到分层解耦,这样如果换框架,只修改网络请求这层的封装就可以了,那么现在我们需要将 Alamofire
封装成 Network
,再把 Network
弄到我们的 BaseComponent
里面去,怎么做呢?
现在先将 Network
拖到 BaseComponent
的 Classes
目录中,因为 BaseComponent
的测试工程没有 Alamofire
,所以 Network
肯定是会报错了,不要慌,下面我们修改 spec
文件:
Pod::Spec.new do |s|
s.name = 'BaseComponent'
s.version = '0.2.0'
s.summary = '基础组价'
s.description = '包括基本配置,常量,扩展,工具类等'
s.homepage = 'https://coding.net/u/tianziyao/p/BaseComponent'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'tianziyao' => 'ziyao.tian@gmail.com' }
s.source = { :git => 'https://git.coding.net/tianziyao/BaseComponent.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'BaseComponent/Classes/**/*'
s.dependency 'Alamofire'
s.dependency 'SDWebImage'
end
dependency
指明了这个库的依赖,改好之后 pod install
,Alamofire
就安装到了 BaseComponent
的测试工程中,现在就可以使用 Alamofire
进行网络请求封装,直接 import
就可以了:
import Foundation
import Alamofire
import SDWebImage
open class Network {
open class func request(url: String, parameters: [String:Any]?) {
Alamofire.request(url, method: .get, parameters: parameters).responseJSON { (response) in
guard let JSON = response.result.value else { return }
print(JSON)
}
}
}
extension UIImageView {
public func image(with url: URL?) {
self.sd_setImage(with: url)
}
}
现在再进行一次远程私有库升级,整个依赖就做好了,需要注意的是,已经做了依赖的话,相关的库就可以从 Podfile
文件中去掉了:
project 'Ting.xcodeproj'
source 'https://github.com/CocoaPods/Specs.git'
source 'git@git.coding.net:tianziyao/TZYSpecs.git'
target 'Ting' do
use_frameworks!
pod 'BaseComponent'
# pod 'Alamofire'
target 'TingTests' do
inherit! :search_paths
end
target 'TingUITests' do
inherit! :search_paths
end
end
现在是我们依赖的是公开库,直接升级 CocoaPods 私有库就可以,但是如果依赖的是另外一个私有库,这个依赖关系最终还要上传到私有索引库中,这样其他人在使用的时候才会知道这个依赖关系,现在走一下升级的流程,你会得到类似这个报错:
[!] The `TargetComponent.podspec` specification does not validate.
这个报错是因为 TargetComponent
这个库没有在官方的索引库当中,忽略就可以了,当然,在使用的时候,TargetComponent
这个库可以在你的本地索引文件中找到,否则无法使用。
CocoaPods 资源依赖
现在我们可以让一个库依赖另外一个库,但是看下面这段代码:
/// 获取中间的视图
open class func tabBarMiddleView() -> TZYTabBarMiddleView {
let view = Bundle.main.loadNibNamed("TZYTabBarMiddleView", owner: nil, options: nil)?.first
return (view as? TZYTabBarMiddleView) ?? TZYTabBarMiddleView()
}
这段代码读取了一个 XIB 文件,这个库的结构是这样的:
.
├── Assets
└── Classes
└── MainModule
├── Controller
│ ├── TZYNavBarC.swift
│ └── TZYTabBarC.swift
└── View
├── TZYNavBar.swift
├── TZYTabBar.swift
├── TZYTabBarMiddleView.swift
└── TZYTabBarMiddleView.xib
我们可以成功调用这个方法吗?不能,因为 TZYTabBarMiddleView.xib
这个文件的 Target 是 MainModule
,使用 CocoaPods 把这个库安装到我们项目后,XIB 文件即使在,也是在 Pods 这个工程里,而我们在壳工程中使用 TZYTabBarMiddleView.xib
,也是必然找不到的。
下面我们把模板库的测试工程编译一下,打开 Products 目录下的 .app
文件,看一下文件结构:
.
├── Base.lproj
│ ├── LaunchScreen.nib
│ └── Main.storyboardc
├── Frameworks
│ ├── Alamofire.framework
│ │ ├── Alamofire
│ │ ├── Info.plist
│ │ └── _CodeSignature
│ │ └── CodeResources
│ ├── BaseComponent.framework
│ │ ├── BaseComponent
│ │ ├── Info.plist
│ │ └── _CodeSignature
│ │ └── CodeResources
│ ├── SDWebImage.framework
│ │ ├── Info.plist
│ │ ├── SDWebImage
│ │ └── _CodeSignature
│ │ └── CodeResources
│ ├── TargetComponent.framework
│ │ ├── Info.plist
│ │ ├── TZYTabBarMiddleView.nib
│ │ ├── TargetComponent
│ │ └── _CodeSignature
│ │ └── CodeResources
├── Info.plist
├── PkgInfo
├── TargetComponent_Example
├── _CodeSignature
│ └── CodeResources
└── libswiftRemoteMirror.dylib
通过路径可以看到,TZYTabBarMiddleView.nib
是在:
mainBundle/Frameworks/TargetComponent.framework
这个路径下面,因此 mainBundle.loadXIb
肯定是找不到资源文件的,那么该如何修改呢?
open class func tabBarMiddleView() -> TZYTabBarMiddleView {
let view = Bundle(for: TZYTabBarMiddleView.self).loadNibNamed("TZYTabBarMiddleView", owner: nil, options: nil)?.first
//let view = Bundle.main.loadNibNamed("TZYTabBarMiddleView", owner: nil, options: nil)?.first
return (view as? TZYTabBarMiddleView) ?? TZYTabBarMiddleView()
}
这部分的重点就是 Bundle(for aClass: Swift.AnyClass)
这个方法。
CocoaPods 图片依赖
上面我们讲到了怎样使用 Pod 库里面的 XIB 文件,但是还有其他资源文件,例如图片、音频、视频,图片我们一般是放在 Assets.xcassets
,但是 Pod 库并没有对应的路径,那么它所需要的图片放在哪里,已经如何使用呢?现在使用 pod lib create
命令创建一个 Pod 库,进入以下路径:
组件名/Assets
把一些图片拖入到 Assets
文件夹内,然后在 podspec
文件中加入以下代码:
s.resource_bundles = {
'组件名' => ['组件名/Assets/*.png'] //只加载 png 文件
# '组件名' => ['组件名/Assets/*'] //加载所有文件
}
然后执行 pod install
,Pod 库中就出现了之前拖入 Assets
文件夹的图片,但是现在还不能使用,我们先来看一下打包以后这些图片的路径:
.
├── Base.lproj
│ ├── LaunchScreen.nib
│ └── Main.storyboardc
│ ├── Info.plist
│ ├── UIViewController-vXZ-lx-hvc.nib
│ └── vXZ-lx-hvc-view-kh9-bI-dsS.nib
├── Frameworks
│ ├── TargetComponent.framework
│ │ ├── Info.plist
│ │ ├── TZYTabBarMiddleView.nib
│ │ ├── TargetComponent
│ │ ├── TargetComponent.bundle
│ │ │ ├── Info.plist
│ │ │ ├── tabbar_bg_320x49_@3x.png
│ │ │ └── zxy_icon_48x48_@2x.png
│ │ └── _CodeSignature
│ │ └── CodeResources
│ └── libswiftUIKit.dylib
├── Info.plist
├── PkgInfo
├── TargetComponent_Example
├── _CodeSignature
│ └── CodeResources
├── embedded.mobileprovision
└── libswiftRemoteMirror.dylib
可以看到,打包后的路径在:
mainBundle/Frameworks/TargetComponent.framework/TargetComponent.bundle
这个路径下面,而代码中的 UIImage(named: "tabbar_bg")
读取的是 mainBundle
下的资源文件,因此还是找不到的,那么这时使用图片,应该将代码改成这样:
backgroundImage = UIImage.image(withName: "tabbar_bg_320x49_@3x.png")
extension UIImage {
public class func image(withName name: String) -> UIImage? {
let bundle = Bundle(for: UIImage.self)
guard let path = bundle.path(forResource: name, ofType: nil, inDirectory: "TargetComponent.bundle") else {
return nil
}
return UIImage(contentsOfFile: path)
}
}
这里需要注意的是,文件名需要完整。以上是在代码中加载图片,如果是在 XIB 中加载图片,应该怎样做呢?那么再看一下上面的目录结构,TZYTabBarMiddleView.nib
和 TargetComponent.bundle
处于同一个目录,我们可以在 TZYTabBarMiddleView.xib
中通过相对路径,使用 TargetComponent.bundle
里面的图片,因此在 XIB 中,图片名应该是这样的:
TargetComponent.bundle/tabbar_bg_320x49_@3x
CocoaPods 子库
现在我们实现了一个完整的远程私有库,可以升级,依赖其他的库,提供给其他人使用,但是现在还有一点问题,其他人如果要用我们的库,就需要把 BaseComponent
完整的克隆过来,但是他可能只需要 BaseComponent
里面的 Network
,其他的扩展、工具等并不想使用,也不想导入过来,怎么办?有两种方案:
- 把
Network
剥离出来,再单独建一个远程私有库; - 使用子库迁出
Network
;
第一种方案大家已经知道了,就是上面的一大篇,麻烦不说,而且东西一多起来,这里一个库,那里一个库,也不容易管理,所以,下面就有请子库隆重登场。
在开始之前,我们先来开一个东西,下面是 pod search AFN
中的一条记录:
-> AFNetworking (3.1.0)
A delightful iOS and OS X networking framework.
pod 'AFNetworking', '~> 3.1.0'
- Homepage: https://github.com/AFNetworking/AFNetworking
- Source: https://github.com/AFNetworking/AFNetworking.git
- Versions: 3.1.0, 3.0.4, 3.0.3, 3.0.2, 3.0.1, 3.0.0, 3.0.0-beta.3, 3.0.0-beta.2, 3.0.0-beta.1, 2.6.3, 2.6.2, 2.6.1, 2.6.0, 2.5.4, 2.5.3, 2.5.2, 2.5.1, 2.5.0, 2.4.1,
2.4.0, 2.3.1, 2.3.0, 2.2.4, 2.2.3, 2.2.2, 2.2.1, 2.2.0, 2.1.0, 2.0.3, 2.0.2, 2.0.1, 2.0.0, 2.0.0-RC3, 2.0.0-RC2, 2.0.0-RC1, 1.3.4, 1.3.3, 1.3.2, 1.3.1, 1.3.0, 1.2.1,
1.2.0, 1.1.0, 1.0.1, 1.0, 1.0RC3, 1.0RC2, 1.0RC1, 0.10.1, 0.10.0, 0.9.2, 0.9.1, 0.9.0, 0.7.0, 0.5.1 [master repo]
- Subspecs:
- AFNetworking/Serialization (3.1.0)
- AFNetworking/Security (3.1.0)
- AFNetworking/Reachability (3.1.0)
- AFNetworking/NSURLSession (3.1.0)
- AFNetworking/UIKit (3.1.0)
注意 Subspecs
这里,它就是本节要讲的东西,首先将 spec
改成下面这样:
Pod::Spec.new do |s|
s.name = 'BaseComponent'
s.version = '0.4.0'
s.summary = '基础组价'
s.description = '包括基本配置,常量,扩展,工具类等'
s.homepage = 'https://coding.net/u/tianziyao/p/BaseComponent'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'tianziyao' => 'ziyao.tian@gmail.com' }
s.source = { :git => 'https://git.coding.net/tianziyao/BaseComponent.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
# s.source_files = 'BaseComponent/Classes/**/*'
s.subspec 'Network' do |n|
n.source_files = 'BaseComponent/Classes/Network/**/*'
n.dependency 'Alamofire'
n.dependency 'SDWebImage'
end
s.subspec 'Const' do |c|
c.source_files = 'BaseComponent/Classes/Const/**/*'
end
s.subspec 'Extension' do |e|
e.source_files = 'BaseComponent/Classes/Extension/**/*'
end
s.subspec 'Tool' do |t|
t.source_files = 'BaseComponent/Classes/Tool/**/*'
end
end
在这里要注意 source_files
和 dependency
以及版本的变化,修改完成推到远程索引库,并打好 0.4.0
的分支,执行:
pod spec lint
pod repo push TZYSpecs BaseComponent.podspec
pod update --no-repo-update
pod search Base
现在就可以找到了:
-> BaseComponent (0.4.0)
基础组价
pod 'BaseComponent', '~> 0.4.0'
- Homepage: https://coding.net/u/tianziyao/p/BaseComponent
- Source: https://git.coding.net/tianziyao/BaseComponent.git
- Versions: 0.4.0, 0.3.0, 0.2.0, 0.1.0 [TZYSpecs repo]
- Subspecs:
- BaseComponent/Network (0.4.0)
- BaseComponent/Const (0.4.0)
- BaseComponent/Extension (0.4.0)
- BaseComponent/Tool (0.4.0)
那么如何使用呢?把 Podfile
改成这样:
project 'Ting.xcodeproj'
source 'https://github.com/CocoaPods/Specs.git'
source 'git@git.coding.net:tianziyao/TZYSpecs.git'
target 'Ting' do
use_frameworks!
pod 'BaseComponent/Network'
# 也可以用下面的写法
# pod 'BaseComponent', :subspecs => ['Network', 'Extension']
target 'TingTests' do
inherit! :search_paths
end
target 'TingUITests' do
inherit! :search_paths
end
end
现在 pod install
,就完成了子库的创建和使用。
结尾
这篇文章断断续续写了快一周,其实一般我们用不到 CocoaPods 这些功能,不过了解一下 CocoaPods 的工作原理也是没有坏处的。
这篇文章主要是为了使用 CocoaPods 进行组件化开发,关于组件化开发的思想,可以看下面这篇文章: