(原创)组件化:发布CocoaPods私有/公有库

文章目录

简书的目录跳转还在研究中...

  1. 什么是CocoaPods?
    • CocoaPods的核心组件
  2. podspec文件?
    • podspec是如何下载资源文件?
    • 如何创建podspec文件?
    • podspec如何编辑?
      • 如何做文件夹层级?
      • 如何关联资源文件?
      • 编写swift版本库注意事项
  3. podspec准备就绪,如何发布出去呢?
    • 发布公有库
    • 发布私有库
  4. 发布完成去使用
    • 发布完搜索不到怎么办?
    • 搜索到没法下载如何是好?
  5. 常见发布问题

什么是CocoaPods?

CocoaPods 是开发 OS X 和 iOS 应用程序的一个第三方库的依赖管理工具。利用 CocoaPods,可以定义自己的依赖关系 (称作pods),并且随着时间的变化,以及在整个开发环境中对第三方库的版本管理非常方便。
CocoaPods 背后的理念主要体现在两个方面。首先,在工程中引入第三方代码会涉及到许多内容。针对 Objective-C 初级开发者来说,工程文件的配置会让人很沮丧。在配置 build phases 和 linker flags 过程中,会引起许多人为因素的错误。CocoaPods 简化了这一切,它能够自动配置编译选项。
其次,通过 CocoaPods,可以很方便的查找到新的第三方库。当然,这并不是说你可以简单的将别人提供的库拿来拼凑成一个应用程序。它的真正作用是让你能够找到真正好用的库,以此来缩短我们的开发周期和提升软件的质量。
CocoaPods是用 Ruby 写的,并由若干个 Ruby 包 (gems) 构成的。在解析整合过程中,最重要的几个 gems 分别是:CocoaPods/CocoaPods,CocoaPods/Core, 和CocoaPods/Xcodeproj(是的,CocoaPods 是一个依赖管理工具 -- 利用依赖管理进行构建的!)。
编者注CocoaPods 是一个 objc 的依赖管理工具,而其本身是利用 ruby 的依赖管理 gem 进行构建的。

CocoaPods的核心组件
组件 说明
CocoaPods/CocoaPod 这是是一个面向用户的组件,每当执行一个pod命令时,这个组件都将被激活。该组件包括了所有使用 CocoaPods 涉及到的功能,并且还能通过调用所有其它的 gems 来执行任务
CocoaPods/Core Core 组件提供支持与 CocoaPods 相关文件的处理,文件主要是 Podfile 和 podspecs
Podfile Podfile 是一个文件,用于定义项目所需要使用的第三方库。该文件支持高度定制,你可以根据个人喜好对其做出定制。更多相关信息,请查阅Podfile 指南
CocoaPods/Xcodeproj 这个 gem 组件负责所有工程文件的整合。它能够对创建并修改.xcodeproj和.xcworkspace文件。它也可以作为单独的一个 gem 包使用。如果你想要写一个脚本来方便的修改工程文件,那么可以使用这个 gem
Manifest.lock 这是每次运行pod install命令时创建的Podfile.lock文件的副本。如果你遇见过这样的错误沙盒文件与 Podfile.lock 文件不同步 (The sandbox is not in sync with the Podfile.lock),这是因为 Manifest.lock 文件和Podfile.lock文件不一致所引起。由于Pods所在的目录并不总在版本控制之下,这样可以保证开发者运行 app 之前都能更新他们的 pods,否则 app 可能会 crash,或者在一些不太明显的地方编译失败
xcproj 如果你已经依照我们的建议在系统上安装了xcproj,它会对Pods.xcodeproj文件执行一下touch以将其转换成为旧的 ASCII plist 格式的文件。为什么要这么做呢?虽然在很久以前就不被其它软件支持了,但是 Xcode 仍然依赖于这种格式。如果没有 xcproj,你的Pods.xcodeproj文件将会以 XML 格式的 plist 文件存储,当你用 Xcode 打开它时,它会被改写,并造成大量的文件改动
Podfile.lock 这是 CocoaPods 创建的最重要的文件之一。它记录了需要被安装的 pod 的每个已安装的版本。如果你想知道已安装的 pod 是哪个版本,可以查看这个文件。推荐将 Podfile.lock 文件加入到版本控制中,这有助于整个团队的一致性
Podspec .podspec也是一个文件,该文件描述了一个库是怎样被添加到工程中的。它支持的功能有:列出源文件、framework、编译选项和某个库所需要的依赖等
关于podfile指定版本的说明
版本 说明
pod ‘Alamofire’ 不指定依赖库版本,表示每次都获取最新版本
pod ‘Alamofire’, ‘2.0’ 只使用2.0版本
pod ‘Alamofire’, ‘> 2.0’ 使用高于2.0的版本
pod ‘Alamofire’, ‘>= 2.0’ 使用大于或等于2.0的版本
pod ‘Alamofire’, ‘< 2.0’ 使用小于2.0的版本
pod ‘Alamofire’, ‘<= 2.0’ 使用小于或等于2.0的版本
pod ‘Alamofire’, ‘~> 0.1.2’ 使用大于等于0.1.2但小于0.2的版本
pod ‘Alamofire’, ‘~>0.1’ 使用大于等于0.1但小于1.0的版本
pod ‘Alamofire’, ‘~>0’ 高于0的版本,等同于表示每次都获取最新版本

podspec文件

一. 什么是podspec文件?

  • podspec中spec的全称是“Specification”,说明书的意思。顾名思义,这是用来描述这个库说明信息的文件。
  • podspec是cocoaPods的一种文件格式,有一套自己的语法,我们可以到cocoaPods官网进行详细了解。
  • podspec描述了一个pod库的版本。它包括有关应从何处获取源、使用什么文件、应用的构建设置以及其他一般元数据(如其名称、版本和描述)的详细信息。
1. cocoapods是如何通过podspec下载资源文件?

podspec 管理几个重要信息
文件源所在git地址,源文件所在git地址的文件路径,源文件对应的tag值
通过以上三个内容,就精准的掌握了资源文件
然后通过下面的命令即可下载想要的资源。

  • pod install
    当我们新建一个Podfile文件运行后,会自动生成一个Podfile.lock文件,Podfile.lock文件里存储着我们已经安装的依赖库(pods)的版本。
    当我们第一次运行Podfile时,如果对依赖库不指定版本的话,cocoapods会安装最新的版本,同时将pods的版本记录在Podfile.lock文件中。这个文件会保持对每个pod已安装版本的跟踪,并且锁定这些版本。
    再执行pod install的话,只会处理没有记录在Podfile.lock中的依赖库,会查找匹配Podfile中描述的版本。对于已经记录在Podfile.lock的依赖库,会下载Podfile.lock文件中记录的版本,而不会检查是否有更新。当然,如果你约束了pods的版本的话,会按照你指定的版本进行安装,同时也会更新Podfile.lock记录的信息。
    简单的说:使用pod install在你的工程里安装新的pods。尽管你已经有了一个Podfile并且之前运行过pod install;尽管你只是在已经使用了CocoaPods的工程中添加/移除了pod,你都要使用pod instal

  • pod outdated
    检查记录在Podfile.lock文件中的pod版本更新情况。
    当你运行pod outdated命令,CocoaPods会列出所有有新版本的pods,新版本是指比记录在Podfile.lock文件中的版本(即当前每个pod安装的版本)更新的版本。这意味着如果你对这些pod运行pod update PODNAME,它们将会更新。

  • pod update
    推荐用法:pod update + podName
    当你运行pod update podName命令,CocoaPods将会尝试找到PODNAME指定pod的更新版本,而不会顾及Podfile.lock文件中记录的版本。它将会更新pod的最新可用版本.如果你运行pod update而不带pod名称,CocoaPods将会更新Podfile文件中记录的每一个pod到最新可用版本.
    简单的说:只有在你想要更新pods到新的版本时才使用pod update podName

  • 更新缓慢 ?
    最近使用CocoaPods来添加第三方类库,无论是执行pod install还是pod update都卡在了Analyzing dependencies不动. 原因在于当执行以上两个命令的时候会升级CocoaPods的spec仓库,加一个参数可以省略这一步,然后速度就会提升不少。加参数的命令如下:
    pod install --verbose --no-repo-update
    pod update --verbose --no-repo-update
    但这样处理,我额外发现了一个弊端:
    就是当你搜索一个库时,往往搜出来不是最新的版本.请自行更新。
    --no-repo-update 不更新本地的仓库,直接更新源。
    --verbose输出详细日志信息

  • 明明线上有这个库,但是pod search podName却搜索不到?
    open ~/Library/Caches/CocoaPods 删除该目录下的search_index.json文件。
    本地会保存一个repo镜像,search操作只是为了方便会到本地去检索,对应的应该会有个策略,看起来应该就是缓存里这个plist了,以后如果repos更新了,plist里面没有更新,那么就会有问题了。

2. 如何创建podspec文件?

说一个最简单最实用的方法 通过pod lib create + 名字 来创建

创建步骤

代码和资源在创建的项目中的pods -> Development Pods中编写。


代码位置

说明1: 用该方法创建的项目,默认遵守了MIT License协议
说明2: 注意层级,podfile文件在Example文件夹下,podspec文件在项目根目录下

3. podspec如何编辑?

podspec文件关键字说明

#  基本信息的配置
name:框架名
version:当前版本(注意,是当前版本,假如你后续更新了新版本,需要修改此处)
summary:简要描述,在pod search的时候会显示该信息。
description:详细描述
homepage:页面链接
license:开源协议
author:作者
platform:支持最低ios版本
swift_version : swift对应的版本

# 源文件的配置
source:源码git地址
source_files:源文件(可以包含.h和.m)
subspec:子库
public_header_files:头文件(.h文件)
resource_bundles:资源文件(配置的文件会放到你自己指定的bundle中)

# 依赖的配置
frameworks:依赖的系统框架
vendored_frameworks:依赖的非系统框架
libraries:依赖的系统库
vendored_libraries:依赖的非系统的静态库
dependency:依赖的三方库

示例说明

Pod::Spec.new do |s|
  s.name             = 'PodspecDemo'               #库的名字
  s.version          = '0.0.1'                     #库的版本号
  s.summary          = 'short description'         # 库的简短描述信息
  s.homepage         = 'https://gitee.com/modularComponents/Tools'       #主页地址
  s.license          = { :type => 'MIT', :file => 'LICENSE' }            #协议
  s.author           = { 'Mccc' => '562863544@qq.com' }                  #作者信息
  s.platform         = :ios, '8.0'                   #平台                      
  s.ios.deployment_target = '8.0'                    #最低支持的target
  s.swift_version = '4.0'                            #支持的swift版本
  s.description      = 'long description'            #库的详细描述信息

  s.source           = { :git => 'https://gitee.com/PodspecDemo.git', :tag => s.version.to_s }   #资源的git地址和版本号
  s.source_files     = 'PodspecDemo/Classes/**/*'  # git地址下资源文件的路径


# =======以下为可选 ===========
# 依赖
  s.dependency  'SnapKit'   #此库需要依赖SnapKit

# 加载资源文件
  s.ios.resource_bundle = { 'ToolsModuleBundle' => 'ToolsModule/Assets/Home/**/*.{png,jpg}' }  #添加资源文件

  # 设置文件夹分层
  s.subspec 'compass' do |ss|
      ss.source_files = "ToolsModule/Classes/Home/compass/*.{h,m}"
  end
end

主要说明几个点:

1). source_files
  • source_files 源码仓库地址(s.source)下,资源文件(要提供给别人使用的所有文件)的路径。

  • 文件路径是以.podspec文件所在层级开始的。

  • source_files写法说明 (以物理路径为准)
    way1:直接指定文件名 source_files = 'XXX/XXX/fileName.swift'
    way2:目录下所有符合的文件 source_files = 'XXX/XXX/*.swift'
    way3: 指定文件夹下所有符合条件的文件source_files = 'XXX/XXX/**/*.{h,m}'
    way4: 指定文件夹下所有文件source_files = 'XXX/XXX/**/*' **/*表示XXX/XXX/目录及其子目录下所有文件

    **表示匹配该目录以及所有子目录,*.png表示所有以.png为扩展名的图片文件。

2). dependency 依赖三方的配置
  • 依赖公共库。
    s.dependency 'SnapKit' #当前依赖库,pod install时会自动下载该库(注意:没有=
  • 依赖私有库。
    s.dependency 'MCPageViewController'
    需要在podfile文件中指定存放podspec文件的仓库地址(包括公有库)
source 'https://github.com/CocoaPods/Specs.git'        #官方仓库地址
source 'https://gitee.com/mancong/MyPublicRepo.git'    #私有仓库地址
  • 如果有依赖私有库情况,检验podspec文件不通过的情况。请使用:
    pod spec lint --sources='spec仓库的地址 如有多个用,隔开。如果公有库,地址写https://github.com/CocoaPods/Specs.git
    比如依赖了两个库MCPageViewController (私有)SnapKit (共有)
    pod spec lint --sources='https://github.com/mancongiOS/MCPageViewControllerRepo.git,https://github.com/CocoaPods/Specs.git' --allow-warnings
    
    --sources : 指定文件源
    --allow-warnings : 允许源文件中有警告的存在

因为CocoaPods会默认是官方地址下查找资源。但是私有库资源不在官方地址下的,所以要指定源地址。

3). 如何做文件夹层级?
  s.subspec 'LevelDevice' do |ss|
      ss.source_files = "ToolsModule/Classes/Home/LevelDevice/*.{h,m}"
      ss.dependency 'ToolsModule/compass'  #依赖本库的其他子库
  end

子库的依赖是独立的,如果父库已经依赖了,子库可以不必依赖。注意依赖其他子subspec的情况,具体看上面提供的podspec示例文件

4). 如何关联资源文件?

因为资源不在主命名空间下,所以需要特别处理一下。

  • 先将资源打包为Bundle
   s.ios.resource_bundle = { 'ToolsModuleBundle' => 'ToolsModule/Assets/Home/**/*.{png,jpg}' }

ToolsModuleBundle是自己命名的bundle名称。提交后会自动生成一个 Resources文件夹,里面包含了资源文件。

  • 如何在不同命名空间下加载
    资源以及打包成一个bundle了,如何在pods库中使用呢?
@objc extension Bundle {
    
    /**
     * 加载指定bundle下的图片资源
     * 在哪个pod下的哪个bundle下的image
     */
    public static func mc_loadImage(_ imageName: String, from bundleName: String, in podName: String) -> UIImage? {
        
        
        var associateBundleURL = Bundle.main.url(forResource: "Frameworks", withExtension: nil)
        associateBundleURL = associateBundleURL?.appendingPathComponent(podName)
        associateBundleURL = associateBundleURL?.appendingPathExtension("framework")
        
        
        if associateBundleURL == nil {
            print("获取bundle失败")
            return nil
        }
        
        let associateBunle = Bundle.init(url: associateBundleURL!)
        associateBundleURL = associateBunle?.url(forResource: bundleName, withExtension: "bundle")
        
        if associateBundleURL != nil {
            let bundle = Bundle.init(url: associateBundleURL!)
            let scale = Int(UIScreen.main.scale)
            
            // 适配2x还是3x图片
            let name = imageName + "@" + String(scale) + "x"
            let path = bundle?.path(forResource: name, ofType: "png")
            
            if path == nil {
                print("获取bundle失败")
                return nil
            }
            let image1 = UIImage.init(contentsOfFile: path!)
            return image1

        } else {
            return nil
        }
    }
}

// 使用
 let image1 = Bundle.mc_loadImage("图片名", from: "命名的bundle名字", in: "库名字")
5). 编写swift版本库注意事项
  • 配置swift的版本
    s.swift_version = '4.0' 因为不同版本对应不同的swift方法。比如privite这个作用域修饰词3.0上就没有。
  • 配置iOS的部署版本
    s.ios.deployment_target = '8.0'
  • 给swift文件中的类名和方法添加作用域修饰词
    需要给类加上修饰词,只允许访问不能继承就用public,如何允许继承就用open修饰,不允许被继承就用public修饰。如果不用这两个关键词修饰的类或者方法,都不会被其他module访问到。
6). 如何校验podspec文件
  • pod lib lint
    只从本地验证你的pod能否通过验证。
  • pod spec lint
    从本地和远程验证你的pod能否通过验证,建议使用这个。
  • --use-libraries
    依赖库(s.dependency)中 如果包含.a文件,验证的时候出现错误。加上这个来让验证通过。
  • --allow-warnings
    如何库代码中有警告导致通不过验证。加上这个忽略警告
  • 私有库的验证
    使用pod spec lint去验证私有库能否通过验证时应该,应该要添加--sources并在podfile文件中指定repo的地址,不然会出现找不到repo的错误。
    pod spec lint --sources='私有仓库repo地址1,私有仓库repo地址1,公有库的地址'
    用英文逗号隔开公有库地址为:https://github.com/CocoaPods/Specs repo的地址为存放podspec文件的仓库podfile文件中指定repo地址

podspec准备就绪,如何发布出去呢?

1. 为什么要发布podspec文件?

我们已经将代码源用podspec文件管理起来了。要让其他人能下载使用你的代码,就要让他找到podspec文件,所以我们要讲podspec文件放在网络上托管起来。

ps: 也就是说,一份库,需要两个git仓库来托管,一个是代码源的仓库,一个是podspec的仓库

3. 发布前的准备。
  • 在Example目录下,pod install一下。
  • 回到根目录(podspec所在层级目录),对podspec文件进行校验。
  • 正常提交代码并打上标签。
// Example目录下
pod install

// 回到项目根目录下
git add -A
git commit -m "此次内容"
git push
git tag 0.0.1
git push origin --tags

发布前的准备工作完成。

2. 发布podspec文件到公有库中
  • 注册cocoaPods
终端执行:$ pod trunk register 邮箱地址 '用户名' --verbose

这里我们一般使用github邮箱和用户名, 然后在你的邮箱中会收到确认邮件, 在浏览器中点击链接确认即注册成功;

  • 验证cocoaPods
    确保打开了邮件中的链接之后(正常加载出来就行),继续执行命令
 终端执行:  $ pod trunk me
  • 发布
 终端执行:  $ pod trunk push XXX.podspec

该命令执行的过程

a.更新本地 pods库 ~/.cocoaPods.repo/master
b.验证*.podspec格式是否正确
c.将 *.podspec 文件转成 JSON 格式
d.对 master 仓库进行合并、提交
  • 完成发布操作
    发布成功,会有成功的提示(包含:日期,链接,Tell your friends)。
3. 发布私有库
  • 创建配置仓(用来存储代码的podspec文件,即上面创建的podspec文件)。创建时别忘记选择MIT License许可证。

  • 将配置仓链接到本地repo
    pod repo add 配置仓名(自己起的仓库名字) 配置仓地址(仓库的git地址)
    示例: pod repo add MCCCSpec https://github.com/mancongiOS/MCCCCSpec.git
    说明:此命令在新开的终端中执行,用完即可关闭

  • 发布私有库
    pod repo push 项目配置库名 项目代码库.podspec
    示例:pod repo push MCCCSpec MCCC.podspec
    说明:此命令在原终端中执行,即podspec同级路径下

  • 发布完成
    发布完成的提示
  • 附录: 查看repos文件夹
    open ~/.cocoapods/repos

    repos

    master对应的公有库版本信息
    非master对应的是私有库版本信息

发布完成去使用

  1. pod search + 库名
    如何搜索不到,请更新本地库的索引文件。
    open ~/Library/Caches/CocoaPods 删除该目录下的search_index.json文件。
    本地会保存一个repo镜像,search操作只是为了方便会到本地去检索,对应的应该会有个策略,看起来应该就是缓存里这个plist了,以后如果repos更新了,plist里面没有更新,那么就搜索不到。
  2. podfile中使用 pod 'MCPageViewController' 报错
    在podfile文件中添加资源文件路径
source 'https://github.com/CocoaPods/Specs.git'        #官方仓库地址
source ‘https://github.com/mancongiOS/MCPageViewControllerRepo.git’ #私有仓库地址
  1. pod install时报错版本信息错误
    更新版本 pod update 库名pod repo update(不推荐使用,会更新所有的第三方库)

常见发布问题

  1. [!] The repoMyPublicRepoat ../../../.cocoapods/repos/MyPublicRepo is not clean
    解决办法
 方法1:
 cd ~/.cocoapods/repos
 cd MyRepo(报错的库名字)
 git clean -f
 方法2:
 pod repo update MyRepo

如果还不可以那么先删除原有的版本库再重新安装版本库,然后执行更新命令

 pod repo remove MyRepo
 pod repo add MyRepo https://xxx.git 
 pod repo push MyRepo XXX.podspec

联系作者

QQ群: 316879774

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

推荐阅读更多精彩内容