本地私有库就是创建一个仓库,将其存储在本地,在本地的其他工程中直接使用。首先在桌面新建一个库,路径如下:
BaseTool/Classes/Network.h
BaseTool/Classes/Network.m
接着创建一个壳工程,现在你的目标是使用 pod 的方式,将 NetWork 这个库集成到壳工程中。
git init
git add.
git commit -m 'x'
和公开库一样,我们需要先创建一个 spec 文件,命令如下:
pod spec create Network
Pod::Spec.new do |s|
s.name = "Network"
s.version = "0.0.1"
s.summary = "Network."
s.homepage = ""
s.license = 'MIT'
s.author = { "goodswifter" => "[email protected]" }
s.source = { :git => "", :tag => "#{s.version}" }
s.source_files = "Classes/**/*.{h,m}"
s.platform = :ios, "9.0" #平台及支持的最低版本
end
现在你的本地库已经准备完毕了,下面就可以使用这个库了。
现在进入到壳工程目录下,执行命令:
pod init
编辑 Podfile
文件,如下:
platform :ios, '9.0'
target 'TestLocalPod' do
use_frameworks!
pod 'Network', :path => '../Network'
end
这里有一个 path
关键字,它表示在 pod install
执行时,在指定的路径下寻找 Network.podspec
文件。
下面执行 pod install 命令,提示信息如下:
Analyzing dependencies
Downloading dependencies
Installing Network (0.0.1)
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
现在 Network
这个库就集成到了壳工程中。
与使用远程库不同,本地库的源文件会在 Development Pods
这个目录下,而不是 Pods
目录,顺便一提,CocoaPods
的库开发,一般也是这样搭建环境的,开发完成后再修改 spec
文件,将其 pull request
到远程索引库。
首先在 LocalLib/Network/
路径下创建一个测试工程 Example
,然后将 Classes
拖到这个测试工程中,这里需要注意的是,Example
和 Classes
是引用关系,不要 Copy
。
简单粗暴的拖拽,现在 Example
工程就可以使用 Network
库了。
将 Network
通过 CocoaPods
安装在 Example
中,和安装在壳工程一样。
看到这里,是不是感觉很烦?就是想做个测试而已,还要拖来拖去,那么繁琐。
不要着急下面来介绍一种更快捷高效的方式,执行下面的命令:
pod lib create ADFMBase
----------------------------
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 ] # 是否需要包含一个Demo
> 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?
> AD
现在我们就有了一个 CocoaPods 的模板工程,它的结构是这样的:
~/Desktop/本地私有库 » tree ADFMBase -L 2 jingshi@goodswifter
ADFMBase
├── ADFMBase
│ ├── Assets
│ └── Classes
│ └── ReplaceMe.m
├── ADFMBase.podspec
├── Example
│ ├── ADFMBase
│ ├── ADFMBase.xcodeproj
│ ├── ADFMBase.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
10 directories, 5 files
看吧,把源码拖到 ReplaceMe.m
的同级目录,执行 pod install
,就完成了本地私有库和其测试工程。
现在使用 pod lib create
就可以方便的生成一个本地私有库了,但是本地私有库有一定的局限性,例如:
远程私有库就可以方便的解决以上的问题,`制作远程私有库分为以下几个步骤:
git
远程仓库;CocoaPods
远程索引库;Pod
所需要的项目工程文件,并上传到 git
远程私有库;podspec
描述文件;CocoaPods
远程索引库提交 podspec
描述文件;Pod
库;git
仓库的创建在此就不在赘述了,本文中我使用 github
做示例,私有 CocoaPods
远程索引库实际上也是一个 git
仓库,现在我们有两个私有库,一个用来存放 Pod
库的源码,一个用来存放 Pod
库的描述文件。
添加私有索引库需要使用 SSH 授权,也是和 Git 仓库一样的,了解的同学可以跳过这一步骤,首先创建公钥:
ssh-keygen
然后找到下面的文件:
~/.ssh/id_rsa.pub
里面存放的字符就是公钥了,然后将公钥添加 github
,链接如下:
https://github.com/settings/keys
现在执行 pod repo
,可以看到下面的信息:
master
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/jingshi/.cocoapods/repos/master
现在我们只有一个 CocoaPods 远程索引库,也是官方的索引库,下面执行:
注意
: 创建私有库的时候一定要创建 .gitignore
文件pod repo add ADFMSpecs https://github.com/goodswifter/ADFMSpecs.git
此时我们的 CocoaPods 远程索引库就安装好了,到下面的路径去看一下:
~/.cocoapods/repos
还记得 pod lib create 命令吗?前面我们使用它来制作了本地私有库,现在它又排上用场了,执行:
pod lib create ADFMBase
源码拖到 ReplaceMe.m
的同级目录,它现在看起来应该是这个样子:
.
├── Assets
└── Classes
└── Category
├── CALayer+PauseAimate.h
├── CALayer+PauseAimate.m
├── UIImage+XMGImage.h
├── UIImage+XMGImage.m
├── UIView+XMGLayout.h
└── UIView+XMGLayout.m
3 directories, 6 files
执行 pod install,就完成了本地私有库和其测试工程,通过测试之后,我们就可以把这个本地私有库制作成远程私有库了。
首先修改 ADFMBase.podspec
文件:
Pod::Spec.new do |s|
s.name = 'ADFMBase'
s.version = '0.0.1'
s.summary = 'ADFMBase.'
s.homepage = 'https://github.com/goodswifter/ADFMBase'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'goodswifter' => '[email protected]' }
s.source = { :git => 'https://github.com/goodswifter/ADFMBase.git', :tag => s.version.to_s }
s.ios.deployment_target = '9.0'
s.source_files = 'ADFMBase/Classes/**/*'
end
然后使用质量检查工具验证一下,保证在 ADFMBase.podspec
路径下,执行:
pod lib lint
根据提示修复就好了
-> ADFMBase (0.0.1)
ADFMBase passed validation.
下面执行:
git add .
git commit -m x
然后和远程仓库进行关联:
git remote add origin https://github.com/goodswifter/ADFMBase.git
git pull origin master
git push origin master
最后打标签
git tag 0.0.1
git push --tags
首先执行下面的命令:
pod spec lint
检验通过后,提示如下:
ADFMBase.podspec passed validation.
然后将 podspec
文件推到远程私有索引库:
pod repo push ADFMSpecs ADFMBase.podspec
pod repo push ADFMSpecs ADFMBase.podspec --allow-warnings
Validating spec
-> ADFMBase (0.0.1)
- WARN | url: The URL (https://github.com/goodswifter/ADFMBasic) is not reachable.
- NOTE | xcodebuild: note: Using new build system
- NOTE | [iOS] xcodebuild: note: Planning build
- NOTE | [iOS] xcodebuild: note: Constructing build description
Updating the `ADFMSpecs' repo
[!] /usr/bin/git -C /Users/jingshi/.cocoapods/repos/ADFMSpecs pull
Your configuration specifies to merge with the ref 'refs/heads/master'
from the remote, but no such ref was fetched.
.gitignore
文件.gitignore
文件即可现在看一下本地索引库中是否已经添加成功:
~/.cocoapods/repos
再看一看你的远程索引库中是否添加成功,现在搜索一下本地索引文件试试:
-> ADFMBase (0.0.1)
ADFMBase.
pod 'ADFMBase', '~> 0.0.1'
- Homepage: https://github.com/goodswifter/ADFMBase
- Source: https://github.com/goodswifter/ADFMBase.git
- Versions: 0.0.1 [ADFMSpecs repo]
现在我们可以找到自己的远程私有库了,下面将 Podfile
文件改成这样:
source 'https://github.com/goodswifter/ADPodSpecs.git'
source 'https://github.com/goodswifter/ADFMSpecs.git'
platform :ios, '9.0'
target 'Test01' do
use_frameworks!
pod 'ADCategories'
pod 'ADFMBase'
end
执行 pod install,整个远程私有库的搭建和使用就完成了。
我们使用远程私有库的目的就是为了版本升级和多人开发,那么远程私有库如何进行升级,升级后其他人又如何使用呢?现在我们给 ADFMBase
进行升级,给它再增加一些功能:
.
ADFMBase
│ ├── Assets
│ └── Classes
│ ├── Base
│ │ ├── Base.h
│ │ ├── Sington.h
│ │ ├── XMGConst.h
│ │ └── XMGConst.m
│ ├── Category
│ │ ├── CALayer+PauseAimate.h
│ │ ├── CALayer+PauseAimate.m
│ │ ├── UIImage+XMGImage.h
│ │ ├── UIImage+XMGImage.m
│ │ ├── UIView+XMGLayout.h
│ │ └── UIView+XMGLayout.m
│ └── Tool
│ ├── XMGAlertTool.h
│ ├── XMGAlertTool.m
│ ├── XMGCacheTool.h
│ ├── XMGCacheTool.m
│ ├── XMGDeviceMessage.h
│ ├── XMGDeviceMessage.m
│ ├── XMGNoticeLocal.h
│ └── XMGNoticeLocal.m
├── ADFMBase.podspec
├── Example
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
在 ADFMBase
的测试工程中测试无误后,将 ADFMBase.podspec
的 version
修改一下:
Pod::Spec.new do |s|
s.name = "ADFMBase"
s.version = "0.1.0"
s.summary = "ADFMBase."
s.homepage = "https://github.com/goodswifter/ADFMBase"
s.license = 'MIT'
s.author = { "goodswifter" => "[email protected]" }
s.source = { :git => "https://github.com/goodswifter/ADFMBase.git", :tag => "#{s.version}" }
s.source_files = "ADFMBase/Classes/**/*.{h,m}"
s.platform = :ios, "9.0" #平台及支持的最低版本
end
现在检查一下私有库是否有错误:
pod lib lint
检查通过后就可以将 ADFMBase
的 0.1.0
版本推到远程私有库中,同时建立 0.1.0
的 Tag。
然后检查一下 spec
文件:
pod spec lint
检查通过后,执行:
pod repo push ADFMSpecs ADFMBase.podspec
如果存在警告,可以执行:
pod repo push ADFMSpecs ADFMBase.podspec --allow-warnings
远程私有库和远程私有索引库全部更新完毕,现在我们回到使用者的视角,这个库可以使用了吗?还不行。
因为本地的索引文件还没有更新,这个源还找不到,现在进入壳工程,执行:
pod update --no-repo-update
ADFMBase
的 0.1.0 版本就乖乖的进入了壳工程。
如果在我们之前的工程中, 大量使用了 ASI
框架, 大家也都知道, ASI
框架不再维护, 需要换成别的网络框架, 例如 AFN
, 但是现在要更换框架的话, ASI
框架在我们的项目中已经无处不再, 这样换的话,我们是不是很痛苦呢?
所以我们一般在开发中都会封装网络请求,做到分层解耦,这样如果换框架,只修改网络请求这层的封装就可以了,那么现在我们需要将 ASI
封装成 Netwok
,再把 Netwok
弄到我们的 ADFMBase
里面去,怎么做呢?
现在先将 Network
拖到 ADFMBase
的 Classes目录中,因为
ADFMBase的测试工程没有
AFN,所以
Network` 肯定是会报错了,不要慌,下面我们修改 spec 文件:
Pod::Spec.new do |s|
s.name = "ADFMBase"
s.version = "0.3.0"
s.summary = "ADFMBase."
s.homepage = "https://github.com/goodswifter/ADFMBase"
s.license = 'MIT'
s.author = { "goodswifter" => "[email protected]" }
s.source = { :git => "https://github.com/goodswifter/ADFMBase.git", :tag => "#{s.version}" }
s.source_files = "ADFMBase/Classes/**/*.{h,m}"
s.platform = :ios, "9.0" #平台及支持的最低版本
s.dependency 'AFNetworking'
end
dependency
指明了这个库的依赖,改好之后 pod install
,AFN
就安装到了 ADFMBase
的测试工程中,现在就可以使用 AFN 进行网络请求封装,直接 import
就可以了
现在再进行一次远程私有库升级,整个依赖就做好了
现在我们实现了一个完整的远程私有库,可以升级,依赖其他的库,提供给其他人使用,但是现在还有一点问题,其他人如果要用我们的库,就需要把 ADFMBase
完整的克隆过来,但是他可能只需要 ADFMBase
里面的 Network
,其他的扩展、工具等并不想使用,也不想导入过来,怎么办?有两种方案:
Network
剥离出来,再单独建一个远程私有库;Network
;第一种方案大家已经知道了,就是上面的一大篇,麻烦不说,而且东西一多起来,这里一个库,那里一个库,也不容易管理,所以,下面就有请子库隆重登场。
注意 Subspecs
这里,它就是本节要讲的东西,首先将 spec
改成下面这样:
Pod::Spec.new do |s|
s.name = "ADFMBase"
s.version = "0.4.0"
s.summary = "ADFMBase."
s.homepage = "https://github.com/goodswifter/ADFMBase"
s.license = 'MIT'
s.author = { "goodswifter" => "[email protected]" }
s.source = { :git => "https://github.com/goodswifter/ADFMBase.git", :tag => "#{s.version}" }
# s.source_files = "ADFMBase/Classes/**/*.{h,m}"
# 子库
s.subspec 'Base' do |b|
b.source_files = 'ADFMBase/Classes/Base/**/*'
end
s.subspec 'Category' do |c|
c.source_files = 'ADFMBase/Classes/Category/**/*'
end
s.subspec 'Network' do |n|
n.source_files = 'ADFMBase/Classes/Network/**/*'
n.dependency 'AFNetworking'
end
s.subspec 'Tool' do |t|
t.source_files = 'ADFMBase/Classes/Tool/**/*'
end
s.platform = :ios, "9.0" #平台及支持的最低版本
end
如果依赖的框架只有某一个子库,使用,要将依赖放到子库的spec中
在这里要注意 source_files 和 dependency 以及版本的变化,修改完成推到远程索引库,并打好 0.4.0 的分支,执行:
pod spec lint
pod repo push ADFMSpecs ADFMBase.podspec
pod update --no-repo-update
pod search ADFMBase
现在我们可以让一个库依赖另外一个库,但是看下面这段代码:
UIView *view = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] firstObject]
这段代码读取了一个 XIB 文件,这个库的结构是这样的:
.
├── Assets
└── Classes
├── Controller
│ ├── ADNavVC.h
│ ├── ADNavVC.m
│ ├── ADTabBarVC.h
│ └── ADTabBarVC.m
└── View
├── ADMiddleView.h
├── ADMiddleView.m
├── ADMiddleView.xib
├── ADNavBar.h
├── ADNavBar.m
├── ADTabBar.h
└── ADTabBar.m
们可以成功调用这个方法吗?不能,因为 ADMiddleView.xib
这个文件的 Target
是 MainModule
,使用 CocoaPods
把这个库安装到我们项目后,XIB
文件即使在,也是在 Pods
这个工程里,而我们在壳工程中使用 ADMiddleView.xib
,也是必然找不到的。
下面我们把模板库的测试工程编译一下,打开 Products
目录下的 .app
文件,看一下文件结构:
.
├── ADFMMain_Example
├── Assets.car
├── Base.lproj
│ └── LaunchScreen.storyboardc
├── Frameworks
│ ├── ADFMMain.framework
│ │ ├── ADFMMain
│ │ ├── ADFMMain.bundle
│ │ │ ├── Info.plist
│ │ │ ├── _CodeSignature
│ │ │ ├── tabbar_bg@2x.png
│ │ │ ├── tabbar_bg@3x.png
│ │ ├── ADMiddleView.nib
│ │ ├── Info.plist
│ │ └── _CodeSignature
├── Info.plist
├── PkgInfo
├── _CodeSignature
└── en.lproj
通过路径可以看到,ADMiddleView.nib
是在:
mainBundle/Frameworks/ADFMMain.framework/ADMiddleView.nib
这个路径下面,因此 mainBundle
中肯定是找不到资源文件的,那么该如何修改呢?
+ (instancetype)middleView {
NSBundle *currentBundle = [NSBundle bundleForClass:self];
return [[currentBundle loadNibNamed:NSStringFromClass(self) owner:nil options:nil] firstObject];
}
这部分的重点就是 [NSBundle bundleForClass:self]
这个方法。
上面我们讲到了怎样使用 Pod 库里面的 XIB 文件,但是还有其他资源文件,例如图片、音频、视频,图片我们一般是放在 Assets.xcassets,但是 Pod 库并没有对应的路径,那么它所需要的图片放在哪里,已经如何使用呢?现在使用 pod lib create 命令创建一个 Pod 库,进入以下路径:
组件名/Assets
把一些图片拖入到 Assets
文件夹内,然后在 podspec
文件中加入以下代码
s.resource_bundles = {
'组件名' => ['组件名/Assets/*.png'] //只加载 png 文件
# '组件名' => ['组件名/Assets/*'] //加载所有文件
}
然后执行 pod install
,Pod
库中就出现了之前拖入 Assets
文件夹的图片,但是现在还不能使用,我们先来看一下打包以后这些图片的路径:
.
├── ADFMMain_Example
├── Assets.car
├── Base.lproj
│ └── LaunchScreen.storyboardc
├── Frameworks
│ ├── ADFMMain.framework
│ │ ├── ADFMMain
│ │ ├── ADFMMain.bundle
│ │ │ ├── Info.plist
│ │ │ ├── _CodeSignature
│ │ │ ├── tabbar_bg@2x.png
│ │ │ ├── tabbar_bg@3x.png
│ │ ├── ADMiddleView.nib
│ │ ├── Info.plist
│ │ └── _CodeSignature
├── Info.plist
├── PkgInfo
├── _CodeSignature
└── en.lproj
可以看到,打包后的路径在:
mainBundle/Frameworks/ADFMMain.framework/ADFMMain.bundle
复制代码这个路径下面,而代码中的 [UIImage imageNamed:@"xxx.png"]
读取的是 mainBundle
下的资源文件,因此还是找不到的,那么这时使用图片,应该将代码改成这样:
NSInteger scale = [[UIScreen mainScreen] scale];
NSBundle *currentBundle = [NSBundle bundleForClass:self];
NSString *fullImageName = [NSString stringWithFormat:@"%@@%zdx", imageName, scale];
NSString *currentBundleName = [currentBundle.infoDictionary[@"CFBundleName"] stringByAppendingString:@".bundle"];
NSString *path = [currentBundle pathForResource:fullImageName ofType:@"png" inDirectory:currentBundleName];
path ? [UIImage imageWithContentsOfFile:path] : nil;
这里需要注意的是,文件名需要完整。以上是在代码中加载图片,如果是在 XIB
中加载图片,应该怎样做呢?那么再看一下上面的目录结构,ADFMMain.nib
和 ADFMMain.bundle
处于同一个目录,我们可以在 ADFMMain.xib
中通过相对路径,使用 ADFMMain.bundle
里面的图片,因此在 XIB 中,图片名应该是这样的:
ADFMMain.bundle/tabbar_bg@2x.png