你真的会用 CocoaPods 吗?

CocoaPods 能够说是 iOS 开发应用最普遍的包管理工具,本篇文章主要介绍 CocoaPods 的第三方库是怎样从网络集成到咱们本地的项目当中,也是制做私有库、开源库和 iOS 项目组件化的一个知识铺垫。html

让咱们从一张图片开始:ios

CocoaPods 工做流程
未命名文件

远程索引库

远程索引库里存放的是各类框架的描述信息,这个库托管在 Github 上,地址以下:git

github.com/CocoaPods/S…github

每一个框架下有数个版本,每一个版本有一个 json 格式的描述信息,以下: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 字段表示该框架的托管地址,也就是上面时序图中的 远程框架库swift

本地索引库

install cocoapods 命令后,须要执行 pod setup 这个命令,pod setup 命令就是将远程索引库克隆到本地来,本地索引库的路径以下:xcode

~/.cocoapods/repos/master
复制代码

本地索引库和远程索引库的目录一致,结构以下:ruby

本地索引库
本地索引库

本地索引文件

当执行 pod search 命令时,若是本地索引文件不存在,会建立这个文件。bash

tianziyaodeMacBook-Air:~ Tian$ pod search afn
Creating search index for spec repo 'master'..
复制代码

若是这个文件存在,则会在此文件中进行索引,确认所须要的框架是否存在,本地索引文件的路径以下:网络

~/资源库/Caches/CocoaPods
复制代码

制做 CocoaPods 库

上面的流程清楚之后,制做 CocoaPods 库相信应该不会太难了,大体分为如下几步:

  1. 托管框架源码到 Git;
  2. 建立框架描述信息;
  3. 上传框架描述信息到 https://github.com/CocoaPods/Specs
  4. 命令行 pod setup , 建立本地索引库;
  5. 命令行 pod install ,将框架集成到项目中;

如今开始动手吧!首先在桌面新建一个 testLib 目录,在该目录下新建一个 Classes 目录,用来存放框架源码,而后将 testLib 托管到 Git。

你能够给 Classes 目录任意的命名,Classes 只是一种约定俗称的命名。

pod spec

pod spec 命令用于建立框架的描述信息文件,文档以下:

guides.cocoapods.org/syntax/pods…

如今在 testLib 目录下执行:

pod spec create testLib
复制代码

目录下会建立一个 testLib.podspec 文件,而后编辑这个文件,主要有如下几个字段:

  • version:这个 spec 映射的版本,保证 Git 的 releases 与此对应;
  • homepage:项目主页;
  • source:框架源代码的托管地址;
  • tag:与 version 对应;
  • source_files:框架源代码的目录、文件、文件类型等规则;

CocoaPods 公开库

根据上面的步骤,如今你须要将生成的 testLib.podspec 文件上传到远程索引库,在此以前,你须要注册一个 Trunk 帐号,文档以下:

guides.cocoapods.org/making/gett…

如今执行下面的命令,记得修改邮箱昵称描述等:

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 拖到这个测试工程中,这里须要注意的是,ExampleClasses 是引用关系,不要 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 文件中主动指明路径;
  • 版本升级不容易维护;
  • 多人开发时,不方便进行合做;

远程私有库就能够方便的解决以上的问题,制做远程私有库分为如下几个步骤:

  1. 建立私有 Git 远程仓库;
  2. 建立私有 CocoaPods 远程索引库;
  3. 建立 Pod 所须要的项目工程文件,并上传到 Git 远程私有库;
  4. 验证 podspec 描述文件;
  5. 向私有 CocoaPods 远程索引库提交 podspec 描述文件;
  6. 使用 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.podspecversion 修改一下:

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
复制代码

检查经过后就能够将 BaseComponent0.2.0 版本推到远程私有库中,同时创建 0.2.0 的 Tag。

而后检查一下 spec 文件:

pod spec lint
复制代码

检查经过后,执行:

pod repo push TZYSpecs BaseComponent.podspec
复制代码

若是存在警告,能够执行:

pod repo push xxx xx.podspec --verbose --allow-warnings
复制代码

远程私有库和远程私有索引库所有更新完毕,如今咱们回到使用者的视角,这个库可使用了吗?还不行。

由于本地的索引文件尚未更新,这个源还找不到,如今进入壳工程,执行:

pod update --no-repo-update
pod install
复制代码

BaseComponent0.2.0 版本就乖乖的进入了壳工程。

CocoaPods 库依赖

在上面的壳工程中,咱们引入了 Alamofire 这个框架,可是若是用着用着忽然以为不爽了,要换框架,这时 Alamofire 的引用在工程中已经无处再也不了,这样换的话是否是很痛苦?

因此咱们通常在开发中都会封装网络请求,作到分层解耦,这样若是换框架,只修改网络请求这层的封装就能够了,那么如今咱们须要将 Alamofire 封装成 Network,再把 Network 弄到咱们的 BaseComponent 里面去,怎么作呢?

如今先将 Network 拖到 BaseComponentClasses 目录中,由于 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 installAlamofire 就安装到了 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 这个库能够在你的本地索引文件中找到,不然没法使用。 保险起见,你也能够在验证和提交时使用 --sources 命令,例如:

pod repo push TZYSpecs TargetComponent.podspec --sources=git@git.coding.net:tianziyao/TZYSpecs.git,master
复制代码

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.nibTargetComponent.bundle 处于同一个目录,咱们能够在 TZYTabBarMiddleView.xib 中经过相对路径,使用 TargetComponent.bundle 里面的图片,所以在 XIB 中,图片名应该是这样的:

TargetComponent.bundle/tabbar_bg_320x49_@3x
复制代码

CocoaPods 子库

如今咱们实现了一个完整的远程私有库,能够升级,依赖其余的库,提供给其余人使用,可是如今还有一点问题,其余人若是要用咱们的库,就须要把 BaseComponent 完整的克隆过来,可是他可能只须要 BaseComponent 里面的 Network,其余的扩展、工具等并不想使用,也不想导入过来,怎么办?有两种方案:

  1. Network 剥离出来,再单独建一个远程私有库;
  2. 使用子库迁出 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_filesdependency 以及版本的变化,修改完成推到远程索引库,并打好 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 进行组件化开发,关于组件化开发的思想,能够看下面这篇文章:

iOS 组件化实践思考

相关文章
相关标签/搜索