03-iOS架构设计|模块化、组件化-依赖包管理工具Cocoapods常用实践:组件库(本地库、远程库(公有、私有))创建、模板工程、划分、资源管理、优化等

11,891 阅读40分钟

前言

在先前的一篇文章,围绕 “移动客户端架构设计"这个大话题展开讨论的过程中,我们做了基本的综述,并且针对 架构设计 的大话题简扼地划分为五个部分:

  • 模块化开发
  • 组件化开发
  • 二进制化处理
  • 设计模式
  • 架构设计与架构演进

我们将在本篇文章,在展开 组件化开发 这个话题之前,先认识一下 组件化开发依赖包管理工具Cocoapods

一、什么是Cocoapods

CocoaPodsSwiftObjective-C Cocoa 项目的依赖管理器

  • 它拥有超过 94,000 个库,用于超过 300 万个应用程序。
  • 利用 Cocoapods,可以定义库自身的依赖关系(简称Pods ),以及在整个开发环境中对第三方库的版本管理非常方便。

CocoaPods 开发者 创造该工具的理念 主要体现在两个方面:

  • 首先,在工程中引入第三方代码会涉及到许多内容。
    • 针对 Objective-C 初级开发者来说,工程文件的配置会让人很沮丧
    • 在配置 build phaseslinker flags 过程中,会引起许多人为因素的错误。
    • CocoaPods 简化了这一切,它能够自动配置编译选项
  • 其次,通过 CocoaPods,可以很方便的查找到新的第三方库。
    • 当然,这并不是说你可以简单的将别人提供的库拿来拼凑成一个应用程序。
    • 它的真正作用是让你能够找到真正好用的库,以此来缩短我们的开发周期和提升软件的质量

二、Cocoapods工作流及管理开发包的几种方式

引入一张图,快速认识 CocoaPods 工作流程:

相关名词介绍

2.1 远程索引库

远程索引库 里存放的是各种框架的描述信息,这个库托管在 Github 上,地址如下: github.com/CocoaPods/S…

  • 每个框架下有数个版本,每个版本有一个 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 字段表示该框架的托管地址,也就是上面时序图中的 远程框架库

2.2 本地索引库

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

~/.cocoapods/repos/master

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

2.3 本地索引文件

当执行 pod search 命令时,如果本地索引文件不存在,会创建这个文件。

pod search afn
Creating search index for spec repo 'master'..

如果这个文件存在,则会在此文件中进行索引,确认所需要的框架是否存在,本地索引文件的路径如下:

~/Library/Caches/CocoaPods

我们在前面的综述部分,已经简扼地介绍了库的几种划分,我们在这里用一个表格来进一步直观描述:

本地库远程库

本地库远程库
私有库开发者本地存放git服务器是私有云
公有库git服务器是公有云

闭源库开源库

闭源库开源库
公有库/私有库代码二进制化处理(.a/.framework/.xcframework)源码、资源文件全部公开

三、在Mac上搭载Cocoapods环境

3.1 在Intel芯片的Mac上安装

1. 安装好Ruby环境/升级ruby环境

  • 1.1 安装RVM: RVM 是一个便捷的多版本 Ruby 环境的管理和切换工具 官网:rvm.io/
    • Mac 中安装 Ruby 需要先安装好 RVM
      • 安装RVM:
         curl -ssl https://get.rvm.io | bash -s stable
        
      • 载入 RVM 环境:
            source ~/.rvm/scripts/rvm
        
      • 修改 RVM 下载 Ruby 的源 为 Ruby China 镜像:
            echo "ruby_url=<https://cache.ruby-china.com/pub/ruby>" > ~/.rvm/user/db
        
      • 检查安装结果(如果能显示版本号,则安装成功 ):
            rvm -v
           ##打印以下结果,代表安装成功: rvm 1.29.9 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [<https://rvm.io/]> 
        
  • 1.2 如果之前安装过 RVM, 则进行RVM 更新:
      rvm get stable
    
  • 1.3 安装 Ruby
    • 1.3.1 列出 Ruby 可安装的版本信息:
          rvm list known
      
    • 1.3.2 安装一个 Ruby 版本
          rvm install 2.6.0
      
    • 1.3.3 如果想设置为默认版本,可以用这条命令来完成:
          rvm use 2.6.0 --default
      
    • 1.3.4 检测一下Ruby:
          ruby -v
      
      image.png
    1. 使用ruby-chinaRuby镜像源
    • 2.1 查看当前Ruby源
         gem sources -l
      
    • 2.2 移除旧的Ruby源
        gem sources --remove https://rubygems.org/
      
    • 2.2 添加新的Ruby源
        gem sources -a https://gems.ruby-china.com/
      
    • 2.3 验证Ruby源是不是Ruby-china
        gem sources -l
      
    1. 安装CocoaPods
        sudo gem install -n /usr/local/bin cocoapods
    
    1. 如果安装了多个Xcode使用下面的命令选择(一般需要选择较新的Xcode版本)
      sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
    
    1. 安装本地库
        pod setup
    
    • 该步骤是在下载Cocoapods官方团队管理的所有远程库索引文件到本地
    • 库托管在 Github 上,地址如下: github.com/CocoaPods/S…
    • 若是能够有索引文件的国内镜像库或者现成的索引文件库文件包,可以直接拷贝放到本地的文件夹
    • 文件夹地址查询命令行: pod repo
      • 查询安装进度命令行
        • cd ~/.cocoapods
        • du -sh *

3.2 在M系列(M1、M1 Pro、M2......)芯片的Mac上安装

  • Cocoapods
    • 安装Cocoapods
          sudo gem install -n /usr/local/bin cocoapods
      
    • 选择Xcode版本 (如果安装了多个Xcode版本,使用下面的命令选择(推荐选择最近的Xcode版本))
        sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer
    
    • 安装本地
      Mac OS Catalina开始,终端执行上面命令后就直接结束了
      我们需要手动安装本地库:
      // 国外地址,如果你能科学上网的话,可以使用这个方法
      git clone https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/trunk
      
      // 清华大学镜像,速度杠杠的,推荐使用
      git clone https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git  ~/.cocoapods/repos/trunk
      

四、Cocoapods的具体使用

4.1 了解Cocoapods在日常开发中的的具体使用

  1. 作为开发包管理工具:
    • 用于 管理 【二方库、三方库(SDK)】依赖包
    • 用于 公司 项目团队内部 组件化开发 管理 公司移动开发套件的【私有库】
    • 开发包 的 管理 除了 库 本身的 代码集成外,还包括【版本控制、编译依赖设置】等
  2. 可以 与 Fastlane + Jenkins 等工具组合 成 简易的 持续集成平台
  3. ......

4.2 初步了解.podspec文件

通过Cocoapods托管的所有依赖包(我们平常称之为Pod库),他们都需要借助一个描述文件来管理:.podspec

  • 使用别人开发的Pod库时,我们通过搜索指令pod search、或集成指令pod install...等指令需要填的 库信息 都是 别人在 他们 发布一个Pod库时,在.podspec写好的;(若是我们只是使用别人的库,可以适当阅读别人的.podspec)
  • 反之,我们若是想发布属于我们公司团队的Pod库,我们也要写好自己Pod库.podspec文件,用于约束和管理我们的Pod库

pod spec 命令创建框架的描述文件``.podspec,文档如下: guides.cocoapods.org/syntax/pods…

创建并初步了解.podspec文件

我们创建一个文件夹命名为NKTestLib
现在在 NKTestLib 目录下执行创建.podspec文件的命令:

pod spec create NKTestLib

创建出来的描述文件:

#
#  Be sure to run `pod spec lint NKTestLib.podspec' to ensure this is a
#  valid spec and to remove all comments including this before submitting the spec.
#
#  To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html
#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |spec|
  # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  These will help people to find your library, and whilst it
  #  can feel like a chore to fill in it's definitely to your advantage. The
  #  summary should be tweet-length, and the description more in depth.
  #
  spec.name         = "NKTestLib"
  spec.version      = "0.0.1"
  spec.summary      = "This is a Pod Lib Demo for blog."
  # 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!
  spec.description  = "一般描述库的用途、主要功能等"
  spec.homepage     = "https://github.com/VanZhang-CN-SZ-SN9037/NKTestLib"
  # spec.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
  # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Licensing your code is important. See https://choosealicense.com for more info.
  #  CocoaPods will detect a license file if there is a named LICENSE*
  #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
  #
  spec.license      = { :type => "MIT", :file => "./LICENSE" }
  # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the authors of the library, with email addresses. Email addresses
  #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
  #  accepts just a name if you'd rather not provide an email address.
  #
  #  Specify a social_media_url where others can refer to, for example a twitter
  #  profile URL.
  #
  spec.author             = { "Van Zhang" => "toBeABetterManZxh91@gmail.com" }
  # Or just: spec.author    = "Van Zhang"
  # spec.authors            = { "Van Zhang" => "Van.Zhang@nike.com" }
  # spec.social_media_url   = "https://twitter.com/Van Zhang"
  # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If this Pod runs only on iOS or OS X, then specify the platform and
  #  the deployment target. You can optionally include the target after the platform.
  #
  spec.platform     = :ios, "14.0"
  # spec.platform     = :ios, "5.0"
  #  When using multiple platforms
  spec.ios.deployment_target = "14.0"
  # spec.osx.deployment_target = "10.7"
  # spec.watchos.deployment_target = "2.0"
  # spec.tvos.deployment_target = "9.0"
  spec.swift_version = "5.4"
  # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the location from where the source should be retrieved.
  #  Supports git, hg, bzr, svn and HTTP.
  #
  spec.source       = { :git => "git@github.com:VanZhang-CN-SZ-SN9037/NKTestLib.git", :tag => "#{spec.version}" }
  # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  CocoaPods is smart about how it includes source code. For source files
  #  giving a folder will include any swift, h, m, mm, c & cpp files.
  #  For header files it will include any header in the folder.
  #  Not including the public_header_files will make all headers public.
  #
  spec.source_files  = "Classes", ".//Classes/*.{c,h,m,mm,swift}"
  spec.exclude_files = "Classes/Exclude"
  spec.public_header_files = "Classes/**/*.h"
  # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  A list of resources included with the Pod. These are copied into the
  #  target bundle with a build phase script. Anything else will be cleaned.
  #  You can preserve files from being cleaned, please don't preserve
  #  non-essential files like tests, examples and documentation.
  #
  # spec.resource  = "icon.png"
  spec.resources = "Classes/Resources/*"
  # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
  # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Link your library with frameworks, or libraries. Libraries do not include
  #  the lib prefix of their name.
  #
  # spec.framework  = "SomeFramework"
  spec.frameworks = ['CoreTelephony','CoreFoundation','AdSupport','AppTrackingTransparency']

  # spec.library   = "iconv"
  spec.libraries = ['c++', 'resolv.9']
  # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If your library depends on compiler flags you can set them in the xcconfig hash
  #  where they will only apply to your library. If you depend on other Podspecs
  #  you can include multiple dependencies to ensure it works.
  # spec.requires_arc = true
 
  # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }

  spec.static_framework = true 

  spec.vendored_frameworks = 'Classes/Vendor/AliTigerTally_IDFA.xcframework'

  spec.dependency "Alamofire", "~>5"
  
end

目录下会创建一个 NKTestLib.podspec 文件,然后编辑这个文件,主要有以下几个字段:

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

在 开发Pod库 过程中,需要用到的描述文件字段:

  • ios.deployment_target: 集成本库,运行使用,要求的系统和最低系统版本
  • swift_version: 限制编译库的Swift版本;
  • resources: 图片等文件资源存放路径
  • frameworks: 需要依赖的系统.framework库
  • libraries: 需要依赖的系统.tbd库
  • static_framework: 传布尔值,描述本Pod库是不是要创建一个文件夹用于存放 .framework静态库
  • vendored_frameworks: 传入第三方库(xxx.framework/xxx.xcframework)的存放路径,集成的时候,会拷贝该路径下的库到framework这个文件夹内
  • vendored_library:传入第三方库(xxx.a)的存放路径,集成的时候,会拷贝该路径下的库到framework这个文件夹内
  • dependency: 依赖库(一般写远程依赖库名称即可)
  • compiler_flags:编译设置(如警告忽略,宏设置)
         ·spec.compiler_flags = '-Wno-format', '-DOS_OBJECT_USE_OBJC=0'
    
  • prefix_header_contents:pch预编译头文件内容
         spec.prefix_header_contents = '#import <UIKit/UIKit.h>'
    
  • prefix_header_file pch预编译头文件路径
        spec.prefix_header_file = 'iphone/include/prefix.pch'
    

五、制作Cocoapods库

在介绍制作Cocoapods库之前,我们先回顾一下 CocoaPods 工作流程: 上面的流程清楚以后,制作 CocoaPods 库相信应该不会太难了,大致分为以下几步:

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

5.1 进一步了解.podspec 文件的语法

长时间不写Podspec文件,容易忘记里面的一些属性含义,所以本文把 官方Podspec语法v1.9.0 翻译出来并且加上了一部分自己的理解,方便后续在用到的时候可以直接拿过来看。

5.1.1 Root specification

root规范存储了相关库特定版本的信息。

下面的属性只能写在root规范上,而不能写在“sub-spec”上。

必填字段:

  • .name:
    pod search 搜索的关键词,这里一定要和.podspec的名称一样,否则会报错;
    spec.name = 'AFNetworking'
    
  • .version:
    Pod库最新的版本
    spec.version = '0.0.1'
    
  • .authors:
    库维护者(而不是Podspec维护者)的名称和电子邮件地址
    spec.author = 'Darth Vader'
    spec.authors = 'Darth Vader', 'Wookiee'
    spec.authors = { 'Darth Vader' => 'darthvader@darkside.com',
                     'Wookiee'     => 'wookiee@aggrrttaaggrrt.com' }
    
  • .license:
    Pod的许可证
    除非源中包含一个名为LICENSE.*LICENCE.*的文件,否则必须指定许可证文件的路径通常用于许可证类型的通知的完整文本。
    如果指定了许可文件,它要么必须是没有文件扩展名或者是一个txtmdmarkdown
    spec.license = 'MIT'
    spec.license = { :type => 'MIT', :file => 'MIT-LICENSE.txt' }
    spec.license = { :type => 'MIT', :text => <<-LICENSE
                       Copyright 2012
                       Permission is granted to...
                     LICENSE
                   }
    
    
  • .homepage:
    Pod主页的URL
    spec.homepage = 'http://www.example.com'
    
  • .source:
    存放检索库的git仓地址位置
    1. 使用标签指定Git来源。多数Podspecs是这样写的。
    spec.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git',
                    :tag => spec.version.to_s }
    
    1. 使用以'v'和子模块为前缀的标签。
    spec.source = { :git => 'https://github.com/typhoon-framework/Typhoon.git',
                    :tag => "v#{spec.version}", :submodules => true }
    
    
    1. 使用带标记的Subversion。
    spec.source = { :svn => 'http://svn.code.sf.net/p/polyclipping/code', :tag => '4.8.8' }
    
    1. 使用与规范的语义版本字符串相同版本的Mercurial。
    spec.source = { :hg => 'https://bitbucket.org/dcutting/hyperbek', :revision => "#{s.version}" }
    
    1. 使用HTTP下载代码的压缩文件。它支持zip,tgz,bz2,txz和tar。
    spec.source = { :http => 'http://dev.wechatapp.com/download/sdk/WeChat_SDK_iOS_en.zip' }
    
    
    1. 使用HTTP下载文件,并使用哈希来验证下载。它支持sha1和sha256。
    spec.source = { :http => 'http://dev.wechatapp.com/download/sdk/WeChat_SDK_iOS_en.zip',
                    :sha1 => '7e21857fe11a511f472cfd7cfa2d979bd7ab7d96' }
    
    我们一般比较常用的就是方式1了,也有2和3
  • .summary:
    简短说明(最多140个字符),该摘要应适当大写并包含正确的标点符号
    spec.summary = 'Computes the meaning of life.'
    

选填字段:

  • .swift_versions:
    支持的Swift版本。CocoaPods将版本“ 4”视为“ 4.0”,而不是“ 4.1”或“ 4.2”
    注意 Swift编译器主要接受主要版本,有时会接受次要版本。尽管CocoaPods允许指定次要版本或补丁版本,但Swift编译器可能不会完全认可它。
    我们一般是直接指定版本

    spec.swift_versions = ['3.0']
    spec.swift_versions = ['3.0', '4.0', '4.2']
    spec.swift_version = '3.0'
    spec.swift_version = '3.0', '4.0'
    
  • .cocoapods_version:
    所支持的CocoaPods版本,比如某个属性,是某个cocoapods版本以上才有的,这个时候进行依赖安装,就需要指定版本。

    spec.cocoapods_version = '>= 0.36'
    
  • .social_media_url:
    Pod、CocoaPods Web服务的社交媒体联系人的URL可以使用此URL

    spec.social_media_url = 'https://twitter.com/cocoapods'
    spec.social_media_url = 'https://groups.google.com/forum/#!forum/cocoapods'
    
  • .description:
    比摘要更详细的说明,用这个字段。我们一般会有README.md和CHANGELOG.md、CODEOWNERS等文件更详细说明来代替它。看你的团队如何抉择

    spec.description = <<-DESC
                         Computes the meaning of life.
                         Features:
                         1. Is self aware
                         ...
                         42. Likes candies.
                       DESC
    
  • .screenshots:
    展示Pod图片的网址列表。用于面向UI库。CocoaPods建议使用gif格式。

    spec.screenshot  = 'http://dl.dropbox.com/u/378729/MBProgressHUD/1.png'
    spec.screenshots = [ 
    'http://dl.dropbox.com/u/378729/MBProgressHUD/1.png',
    'http://dl.dropbox.com/u/378729/MBProgressHUD/2.png' ]
    
  • .documentation_url:
    Pod文档的可选URL,CocoaPods网络媒体资源将使用该URL。将其保留为空白将默认为您的库CocoaDocs生成URL

    spec.documentation_url = 'http://www.example.com/docs.html'
    
  • .prepare_command:
    下载Pod后将执行的bash脚本。该命令可用于创建,删除和修改下载的任何文件,并且将在收集规范其他文件属性的任何路径之前运行。

    在清理Pod和创建Pods项目之前,将执行此命令。工作目录是Pod的根目录。

    如果pod安装了该:path选件,则不会执行此命令。

    spec.prepare_command = 'ruby build_files.rb'
    spec.prepare_command = <<-CMD
                            sed -i 's/MyNameSpacedHeader/Header/g' ./**/*.h
                            sed -i 's/MyNameOtherSpacedHeader/OtherHeader/g' ./**/*.h
                       CMD
    
  • .static_framework:
    use_frameworks!如果指定,则pod应当包含静态库框架。

    spec.static_framework = true
    
  • .deprecated:
    是否已废弃该库

    spec.deprecated = true
    
  • .deprecated_in_favor_of:
    不支持使用的Pod名称

    spec.deprecated_in_favor_of = 'NewMoreAwesomePod'
    

5.1.2 Platform相关

主要是指明支持当前库的平台和相应的部署target。

  • .Platform:
    支持此Pod的平台。保留此空白表示Pod在所有平台上都支持。当支持多个平台时,应改为使用以下deployment_target。
spec.platform = :osx, '10.8'
spec.platform = :ios
spec.platform = :osx
  • .Platdeployment_targetform:
    支持平台的最低部署target。

    platform属性相反,deployment_target 属性允许指定支持该Pod的多个平台-为每个平台指定不同的部署目标。
    spec.ios.deployment_target = '6.0'
    spec.osx.deployment_target = '10.8'
    

5.1.3 Build settings相关

构建环境的配置相关设置

  • .dependency:
    对其他Pod或“sub-spec”的依赖。依赖关系可以指定版本要求

    spec.dependency 'AFNetworking', '~> 1.0'
    spec.dependency 'AFNetworking', '~> 1.0', :configurations => ['Debug']
    spec.dependency 'AFNetworking', '~> 1.0', :configurations => :debug
    spec.dependency 'RestKit/CoreData', '~> 0.20.0'
    spec.ios.dependency 'MBProgressHUD', '~> 0.5'	
    
  • .info_plist:
    要添加到生成的键值对Info.plist

    这些值将与CocoaPods生成的默认值合并,从而覆盖所有重复项。

    对于库规范,值将合并到为使用框架集成的库生成的Info.plist中。它对静态库无效。

    不支持sub-spec(应用和测试spec除外)。

    对于应用程序规范,这些值将合并到应用程序主机的中Info.plist

    对于测试spec,这些值将合并到测试包的中Info.plist

    spec.info_plist = {
      'CFBundleIdentifier' => 'com.myorg.MyLib',
      'MY_VAR' => 'SOME_VALUE'
    }
    
  • .requires_arc:
    允许您指定使用ARC的source_files。它可以是支持ARC的文件,也可以是true,以表示所有source_files都使用ARC。

    不使用ARC的文件将带有-fno-objc-arc编译器标志。

    此属性的默认值为true

    # 默认
    spec.requires_arc = true 
    # 设置
    spec.requires_arc = false
    spec.requires_arc = 'Classes/Arc'
    spec.requires_arc = ['Classes/*ARC.m', 'Classes/ARC.mm']
    
  • .frameworks:
    当前target所需系统framework列表

    spec.ios.framework = 'CFNetwork'
    spec.frameworks = 'QuartzCore', 'CoreData'
    
  • .weak_frameworks:
    当前target所需“弱引用”的framework列表

    spec.weak_framework = 'Twitter'
    spec.weak_frameworks = 'Twitter', 'SafariServices'
    
  • .libraries:
    当前target所需系统library列表

    spec.ios.library = 'xml2'
    spec.libraries = 'xml2', 'z'
    
  • .compiler_flags:
    传递给编译器的flag

    spec.compiler_flags = '-DOS_OBJECT_USE_OBJC=0', '-Wno-format'
    
  • .pod_target_xcconfig:
    要添加到最终私有 pod目标xcconfig文件的任何标志。

    spec.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-lObjC' }
    
  • .user_target_xcconfig:
    不推荐使用

  • .prefix_header_contents:
    不推荐使用

  • .prefix_header_file:
    不推荐使用

  • .module_name:
    用于该spec的框架/ clang模块的名称,而不是默认的名称(如果设置,则为header_dir,否则为规范名称)。

spec.module_name = 'Three20'
  • .header_dir:
    头文件的存储目录。
spec.header_dir = 'Three20Core'
  • .header_mappings_dir:
    用于保留头文件的文件夹结构的目录。如果未提供,则将头文件展平。
spec.header_mappings_dir = 'src/include'
  • .script_phases:
    此属性允许定义脚本,以作为Pod编译的一部分执行。与prepare命令不同,脚本作为xcodebuild的一部分执行,也可以利用在编译期间设置的所有环境变量。

    Pod可以提供要执行的多个脚本,并且将按照声明的顺序添加它们,并考虑它们的执行位置设置。

    注意 为了提供对所有脚本内容的可见性和意识,如果安装了Pod包含任何脚本,则会在安装时向用户显示警告。
spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"' }
spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"', :execution_position => :before_compile }
spec.script_phase = { :name => 'Hello World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' }
spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"',
  :input_files => ['/path/to/input_file.txt'], :output_files => ['/path/to/output_file.txt']
}
spec.script_phase = { :name => 'Hello World', :script => 'echo "Hello World"',
  :input_file_lists => ['/path/to/input_files.xcfilelist'], :output_file_lists => ['/path/to/output_files.xcfilelist']
}
spec.script_phases = [
    { :name => 'Hello World', :script => 'echo "Hello World"' },
    { :name => 'Hello Ruby World', :script => 'puts "Hello World"', :shell_path => '/usr/bin/ruby' },
  ]

5.1.4 File patterns相关

文件路径相关设置;不支持遍历父目录

  • * 匹配所有文件
  • c* 匹配所有以c开头的文件
  • *c 匹配所有以c结尾的文件
  • *c* 将匹配其中包含c的所有文件(包括开头或结尾)

podspec字段:

  • .source_files:
    需要包含的源文件存放路径
    spec.source_files = 'Classes/**/*.{h,m}'
    spec.source_files = 'Classes/**/*.{h,m}', 'More_Classes/**/*.{h,m}'
    
  • .public_header_files:
    用作公共头的文件模式列表。

    这些模式与源文件匹配,以包含将公开给用户项目并从中生成文档的标头。构建库时,这些头将出现在构建目录中。如果未指定公共头,则将source_files中的所有头视为公共。
    spec.public_header_files = 'Headers/Public/*.h'
    
  • .private_header_files:
    用来标记私有文件模式列表。

    这些模式与公共标头(如果未指定公共标头,则与所有标头)匹配,以排除那些不应暴露给用户项目并且不应用于生成文档的标头。构建库时,这些头将出现在构建目录中。

    没有列出为公共和私有的头文件将被视为私有,但除此之外根本不会出现在构建目录中。
    spec.private_header_files = 'Headers/Private/*.h'
    
  • .vendored_frameworks:
    源文件相关联的第三方framework
    spec.ios.vendored_frameworks = 'Frameworks/MyFramework.framework'
    spec.vendored_frameworks = 'MyFramework.framework', 'TheirFramework.framework'
    
  • .vendored_libraries:
    源文件相关联的第三方libraries
    spec.ios.vendored_library = 'Libraries/libProj4.a'
    spec.vendored_libraries = 'libProj4.a', 'libJavaScriptCore.a'
    
  • .resource_bundles:
    为了将Pod构建为静态库,官方强烈建议使用此属性来管理资源文件,因为使用resources属性可能会发生名称冲突。

    资源文件bundle的名称至少应包括Pod的名称,以最大程度地减少名称冲突的可能性。
    spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
    spec.resource_bundles = {
    'MapBox' => ['MapView/Map/Resources/*.png'],
    'MapBoxOtherResources' => ['MapView/Map/OtherResources/*.png']
    }
    
  • .resources:
    复制到target中的资源列表。

    为了将Pod构建为静态库,官方建议是使用resource_bundle,因为使用resources属性可能会发生名称冲突。此外,使用此属性指定的资源将直接复制到客户端目标,因此Xcode不会对其进行优化。
    spec.resource = 'Resources/HockeySDK.bundle'
    spec.resources = ['Images/*.png', 'Sounds/*']
    
  • .exclude_files:
    从其他文件模式中排除的文件模式列表。

    比如在设置某个子模块的时候,不需要包括其中的一些文件,就可以通过这个属性来进行设置。
    spec.ios.exclude_files = 'Classes/osx'
    spec.exclude_files = 'Classes/**/unused.{h,m}'
    
  • .preserve_paths:
    任何被下载的文件之后是不会被移除。

    默认情况下,CocoaPods会移除所有与其他任何文件模式都不匹配的文件。
    spec.preserve_path = 'IMPORTANT.txt'
    spec.preserve_paths = 'Frameworks/*.framework'
    
  • .module_map:
    将此Pod集成为框架时应使用的模块映射文件。

    默认情况下,CocoaPods基于规范中的公共头创建模块映射文件。
    spec.module_map = 'source/module.modulemap'
    

5.1.5 Subspecs相关

一个库可以指定依赖在另一个库、另一个库的子规范或自身的子规范上。
具体看下面这个例子:

pod 'ShareKit', '2.0'
pod 'ShareKit/Twitter',  '2.0'
pod 'ShareKit/Pinboard', '2.0'

我们有时候会编写这样的Podfile文件,导入第三方或者自己依赖的库;那么它对应的podspec文件应该如何编写呢?

如下:

subspec 'Twitter' do |sp|
  sp.source_files = 'Classes/Twitter'
end

subspec 'Pinboard' do |sp|
  sp.source_files = 'Classes/Pinboard'
end

这样写就可以了,指定对应的源关联文件或者资源文件即可。

注意一点,就是通过pod search的时候只能是整个模块,即你不能单独搜索里面的子模块,但是我们podfile依赖是可以指定具体依赖哪一个模块。

  • .default_subspecs:
    指定默认的依赖,如果不指定就依赖全部子依赖。
    spec.default_subspec = 'Core'
    spec.default_subspecs = 'Core', 'UI'
    spec.default_subspecs = :none
    

5.1.6 Multi-Platform support

设置支持的某个平台,比如ios、osx、tvos等

spec.ios.source_files = 'Classes/ios/**/*.{h,m}'
spec.osx.source_files = 'Classes/osx/**/*.{h,m}'
spec.osx.source_files = 'Classes/osx/**/*.{h,m}'
spec.tvos.source_files = 'Classes/tvos/**/*.{h,m}'
spec.watchos.source_files = 'Classes/watchos/**/*.{h,m}'

如果想要知道自己编写的podspec文件是否合法有效,可以通过命令  pod spec lint 进行验证。

5.2 CocoaPods本地库(只有一个主库)

现在开始动手吧!

  • 首先,前面创建的NKTestLib 目录下新建一个 Classes 目录,用来存放框架源码;(可以给 Classes 目录任意的命名,Classes 只是一种约定俗成的命名)
  • 然后,将 NKTestLib 托管到 Git

本地库:
在当前设备创建一个仓库,源码以及相关的资源文件存储在当前电脑,用本地路径导入相关的工程,进行集成使用

~/Desktop/NKTestLib

image.png

接着创建一个壳工程,现在你的目标是使用 pod 的方式,将 NKTestLib 这个库集成到壳工程中。

1.创建库描述文件

和公开库一样,我们需要先创建一个 spec 文件,命令如下:

pod spec create NKTestLib

编辑 NKTestLib.podspec 文件,修改成下面这样:

#
#  Be sure to run `pod spec lint NKTestLib.podspec' to ensure this is a
#  valid spec and to remove all comments including this before submitting the spec.
#
#  To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html
#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#
Pod::Spec.new do |spec|
  # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  These will help people to find your library, and whilst it
  #  can feel like a chore to fill in it's definitely to your advantage. The
  #  summary should be tweet-length, and the description more in depth.
  #
  spec.name         = "NKTestLib"
  spec.version      = "0.0.1"
  spec.summary      = "This is a Pod Lib Demo for blog."
  # 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!
  spec.description  = "一般描述库的用途、主要功能等"
  spec.homepage     = "https://github.com/VanZhang-CN-SZ-SN9037/NKTestLib"
  # spec.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
  # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Licensing your code is important. See https://choosealicense.com for more info.
  #  CocoaPods will detect a license file if there is a named LICENSE*
  #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
  #
  spec.license      = { :type => "MIT", :file => "./LICENSE" }
  # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the authors of the library, with email addresses. Email addresses
  #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
  #  accepts just a name if you'd rather not provide an email address.
  #
  #  Specify a social_media_url where others can refer to, for example a twitter
  #  profile URL.
  #
  spec.author             = { "Van Zhang" => "toBeABetterManZxh91@gmail.com" }
  # Or just: spec.author    = "Van Zhang"
  # spec.authors            = { "Van Zhang" => "Van.Zhang@nike.com" }
  # spec.social_media_url   = "https://twitter.com/Van Zhang"
  # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If this Pod runs only on iOS or OS X, then specify the platform and
  #  the deployment target. You can optionally include the target after the platform.
  #
  spec.platform     = :ios, "14.0"
  # spec.platform     = :ios, "5.0"
  #  When using multiple platforms
  spec.ios.deployment_target = "14.0"
  # spec.osx.deployment_target = "10.7"
  # spec.watchos.deployment_target = "2.0"
  # spec.tvos.deployment_target = "9.0"
  spec.swift_version = "5.4"
  # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the location from where the source should be retrieved.
  #  Supports git, hg, bzr, svn and HTTP.
  #
  spec.source       = { :git => "git@github.com:VanZhang-CN-SZ-SN9037/NKTestLib.git", :tag => "#{spec.version}" }
  # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  CocoaPods is smart about how it includes source code. For source files
  #  giving a folder will include any swift, h, m, mm, c & cpp files.
  #  For header files it will include any header in the folder.
  #  Not including the public_header_files will make all headers public.
  #
  spec.source_files  = "Classes", ".//Classes/*.{c,h,m,mm,swift}"
  spec.exclude_files = "Classes/Exclude"
  spec.public_header_files = "Classes/**/*.h"
  # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  A list of resources included with the Pod. These are copied into the
  #  target bundle with a build phase script. Anything else will be cleaned.
  #  You can preserve files from being cleaned, please don't preserve
  #  non-essential files like tests, examples and documentation.
  #
  # spec.resource  = "icon.png"
  spec.resources = "Classes/Resources/*"
  # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
  # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Link your library with frameworks, or libraries. Libraries do not include
  #  the lib prefix of their name.
  #
  # spec.framework  = "SomeFramework"
  spec.frameworks = ['CoreTelephony','CoreFoundation','AdSupport','AppTrackingTransparency']

  # spec.library   = "iconv"
  spec.libraries = ['c++', 'resolv.9']
  # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If your library depends on compiler flags you can set them in the xcconfig hash
  #  where they will only apply to your library. If you depend on other Podspecs
  #  you can include multiple dependencies to ensure it works.
  # spec.requires_arc = true
 
  # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }

  spec.static_framework = true 

  spec.vendored_frameworks = 'Classes/Vendor/AliTigerTally_IDFA.xcframework'

  spec.dependency "Alamofire", "~>5"
  
end

现在你的本地库已经准备完毕了,下面就可以使用这个库了。

2. 导入本地私有库

现在进入到壳工程目录下,执行命令:

pod init

编辑 Podfile 文件,如下:

# Uncomment the next line to define a global platform for your project

# platform :ios, '9.0'
target 'TempDemo' do

  # Comment the next line if you don't want to use dynamic frameworks

  use_frameworks!

  pod 'NKTestLib',:path=>"/Users/VZha33/Desktop/NKTestLib/NKTestLib.podspec"
 
  # Pods for TempDemo
  
end

这里有一个 path 关键字,它表示在 pod install 执行时,在指定的路径下寻找 NetWork.podspec 文件。

下面执行 pod install 命令,提示信息如下:

pod install
Analyzing dependencies
Downloading dependencies
Installing Alamofire (5.6.4)
Installing NKTestLib (0.0.1)
Generating Pods project
Integrating client project
Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed.

image.png 现在 NKTestLib 这个库就集成到了壳工程中。

与使用远程库不同,本地库的源文件会在 Development Pods 这个目录下,而不是 Pods 目录,顺便一提,CocoaPods 的库开发,一般也是这样搭建环境的,开发完成后再修改 spec 文件,将其 pull request 到远程索引库。

5.3 CocoaPods本地库 (主库+子库)

若在项目中,我们并不需要集成 一个 Pod库的所有功能,只需用到其中一部分功能,我们可以将主库按照不同的功能职责,进一步细化为若干子库,并在目标项目中按需集成。

将主库划分为若干子库:

我们将前面的示例,使用的NKTestLib进一步划分为不同的子库:

  • 修改NKTestLib.podspec文件(为了简洁便于阅读,删掉文件中的部分注释):
    Pod::Spec.new do |spec|
      # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.name         = "NKTestLib"
      spec.version      = "0.0.1"
      spec.summary      = "This is a Pod Lib Demo for blog."
      spec.description  = "一般描述库的用途、主要功能等"
      spec.homepage     = "https://github.com/VanZhang-CN-SZ-SN9037/NKTestLib"
    
      # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.license      = { :type => "MIT", :file => "./LICENSE" }
    
      # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.author             = { "Van Zhang" => "toBeABetterManZxh91@gmail.com" }
    
      # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.platform     = :ios, "14.0"
      spec.swift_version = "5.4"
    
      # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.source       = { :git => "git@github.com:VanZhang-CN-SZ-SN9037/NKTestLib.git", :tag => "#{spec.version}" }
    
      # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #spec.source_files  = "Classes", ".//Classes/*.{c,h,m,mm,swift}"
      #spec.exclude_files = "Classes/Exclude"
      spec.public_header_files = "Classes/**/*.h"
    
      # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.resources = "Assets/*"
    
      # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.frameworks = ['CoreTelephony','CoreFoundation','AdSupport','AppTrackingTransparency']
      spec.libraries = ['c++', 'resolv.9']
    
      # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.static_framework = true 
      spec.vendored_frameworks = 'Classes/Vendor/AliTigerTally_IDFA.xcframework'
    
      #spec.dependency "Alamofire", "~>5"
    
      # ――― Sub Spec Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.subspec 'Network' do |n|
        n.source_files = 'Classes/Network/**/*'
        n.dependency 'Alamofire'
        n.dependency 'SDWebImage'
      end
    
      spec.subspec 'Extension' do |e|
        e.source_files = 'Classes/Extension/**/*'
      end
    
      spec.subspec 'Plugin' do |p|
        p.source_files = 'Classes/Plugin/**/*'
      end
    
      spec.subspec 'CommerceUI' do |c|
        c.source_files = 'Classes/CommerceUI/**/*'
      end
    
    end
    
    • 注意子库的划分语法(子库中所有配置项与主库无差异,只需要将配置项前面的Spec命名替换为子库的即可):
      主库Spec命名.subspec '子库名称' do |子库Spec命名|
        ##示例: 子库的源码路径
        子库Spec命名.source_files = '资源路径'
        ...
      end
    
  • 在本次示例中,将主库进一步划分为4个子库:NetworkExtensionPluginCommerceUI
  • 在需要 集成 Pod库的项目中,按需集成:
    • 全部集成:image.pngimage.png
    • 集成其中任一子库:image.png
      • 集成子库的pod指令写法:
          pod '主库/子库',:path=>"XXX.podspec的本地路径"
          #或者 
          pod '主库',:path=>"XXX.podspec的本地路径",:subspecs => ['子库的Spec名称']
      
      image.png image.png
  • 若是我们有很多个Pod主库,且其中若干个Pod主库内部都有相同的功能,比如网络处理功能,我们还可以把网络处理功能独立出来做一个主库,提供给其它库作为依赖项
  • 若是我们的开发套件比较完善,同一套业务方案,共用同一套变成接口,但会根据不同的需求使用不同的实现方案,我们常常会把库的Interface编程接口Implement业务实现,分别作为一个主库
    • 需要根据不同的需求,使用不同厂商的SDK服务:
    • 一个 项目里面有国际化要求 ,同一类服务在不同的国家地区使用符合法律法规的第三方SDK依赖包进行实现(如电商业务、物流业务,有国内与海外服务)
    • 在同一个业务领域,针对不同的供应商的用户群,类似的业务需求 有一定的定制化业务需求 (如金融领域的银行业务,其金融产品:保险、贷款、信用卡等)
    • ...

5.4 CocoaPods本地库(本地库+Demo工程)

pod spec create指令

我们前面创建本地库的方式是通过:

  • 创建一个文件夹
  • 在文件夹内部放置库代码
  • 通过pod spec create指令给pod库生成描述文件,并根据当前文件夹存放资源的路径修改.podspec

但这种编写pod库的方式,是没有附带Demo工程的且没有单元测试

pod lib create指令

我们还有 另一个 pod 指令:pod lib create 可以用于创建Pod库,该创建方式可以顺便创建Demo工程和单元测试::

    pod lib create XXXXX(你的库名)

image.png

  • 示例:
    ❯ pod lib create NKTestPodLib
    Cloning `https://github.com/CocoaPods/pod-template.git` into `NKTestPodLib`.
    Configuring NKTestPodLib template.
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    
    ------------------------------
    
    To get you started we need to ask a few questions, this should only take a minute.
    
    If this is your first time we recommend running through with the guide:
     - https://guides.cocoapods.org/making/using-pod-lib-create.html
     ( hold cmd and click links to open in a browser. )
    
    
    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 ]
     > 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?
     > NK
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    security: SecKeychainSearchCopyNext: The specified item could not be found in the keychain.
    hint: Using 'master' as the name for the initial branch. This default branch name
    hint: is subject to change. To configure the initial branch name to use in all
    hint: of your new repositories, which will suppress this warning, call:
    hint:
    hint: 	git config --global init.defaultBranch <name>
    hint:
    hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
    hint: 'development'. The just-created branch can be renamed via this command:
    hint:
    hint: 	git branch -m <name>
    
    Running pod install on your new library.
    
    Analyzing dependencies
    Downloading dependencies
    Installing NKTestPodLib (0.1.0)
    Generating Pods project
    Integrating client project
    
    [!] Please close any current Xcode sessions and use `NKTestPodLib.xcworkspace` for this project from now on.
    Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
    
     Ace! you're ready to go!
     We will start you off by opening your project in Xcode
      open 'NKTestPodLib/Example/NKTestPodLib.xcworkspace'
    
    To learn more about the template see `https://github.com/CocoaPods/pod-template.git`.
    To learn more about creating a new pod, see `https://guides.cocoapods.org/making/making-a-cocoapod`.
    

pod lib create指令 + 自定义 Pod库模板

我们 前面通过 pod lib create指令 创建 Pod库,它默认会去根据 Cocoapods官方的Pod库模板进行创建。且这需要不断提问很多个小问题,这步骤很多显得流程相对繁琐;

为了直接一步到位,我们可以根据自定义 Pod库模板,并按照相应的创建方式,一条指令创建pod库:
此处使用的是我个人的 Pod库模板 pod-template

  • Swift Pod 库:
     pod lib create  Pod库名称 Swift --template-url="https://github.com/VanZhang-CN-SZ-SN9037/pod-template"
    
  • OC Pod 库:
     pod lib create  Pod库名称 Objc --template-url="https://github.com/VanZhang-CN-SZ-SN9037/pod-template"
    

5.5 CocoaPods 远程库

私有库远程库公有库远程库

当我们把本地库开发好一个版本之后,若我们想要为外部提供一个Pod库,我们则需要把本地库上传到git云服务器:

  • 公有库: 若是想将开发好的本地Pod库公布出去,提供一个选择给广大开发者,我们需要再开源社区Git,创建一个 代码仓库,一般选择 github
  • 私有库: 若是刚开发好的本地Pod库 仅在 作为 公司 移动开发套件的一部分,在公司内部团队公布,则 需要 在公司部署了Git服务的私有云,创建一个 代码仓库,用于保存
  • 无论是 公有库 还是 私有库, 他们 除了存储的 git仓库 地址不同,还有几点不同:
    • .podspec文件存储不同:
      • 公有库推送到Cocoapods的Podspec
      • 私有库需要在私有云的git另建一个仓库,用于存储私有Pod库的Podspec文件(多个库的Pocspec文件可以存储到同一个Podspec仓里面,其Pod库代码各占一个git仓)

生成一个本地库很容易,但是本地库在管理和使用上有一定的局限性,例如:

  • 需要在 Podfile 文件中主动指明路径;
  • 版本升级不容易维护;
  • 多人开发时,不方便进行合作;

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

  1. 创建 Git 远程仓库;
    • 远程库(私有库):
      • 在git云平台,创建私有代码仓,用于存储Pod库;/在私有云 git 平台中创建代码仓,用于存储Pod库;
    • 远程库(公有库):
      • 在公有云git平台,创建公有代码仓,用于存储Pod库;
  2. 创建 Pod 所需要的项目工程文件,并上传到 Git 代码仓;
  3. 验证 podspec 描述文件;
  4. 提交 podspec 描述文件;
    • 远程库(私有库): 提交到 Cocoapods 官方的 Spec存储代码仓
    • 远程库(公有库): 提交到 个人 创建的 专门存储 私有库 描述文件 Spec的私有仓
  5. 使用 Pod 库;

5.6 CocoaPods 远程库(公有库)

创建公有库远程库

根据前面的步骤,现在你需要将生成的 NKTestLib.podspec 文件上传到远程索引库,在此之前,你需要注册一个 Trunk 账号,文档如下: guides.cocoapods.org/making/gett…

注册 Trunk 账号

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

  • pod trunk register 邮箱地址 ‘名称’ --description=‘MacBook’
    pod trunk register toBeABetterManZxh91@gmail.com 'VanZhang' --description='Macbook Pro'
    
  • 打印[!] Please verify the session by clicking the link in the verification email that has been sent to toBeABetterManZxh91@gmail.com意味着 邮件发送成功,要去邮箱检查 image.png image.png
  • 点击邮箱里的确认链接🔗,你才算真正注册 Trunk 账号成功 image.png

检查 xxx.podspec的合法性

打开邮件里面的链接后,会有类似 you can back to your termainal 的提示,意味着我们可以进行下一步了:

  • cd 到 xxx.podspec 同一目录下
  • pod lib lint 检查 xxx.podspec 的合法性
    pod lib lint --allow-warnings
    
    • 若报错,根据错误提示修复问题
    • 当显示 passed validation 后,说明 xxx.podspec合法,则我们需要把我们本地的xxx.podspec 推送到 Cocoapods存储Specs文件的库 image.png

把 合法的 xxx.podspec 推送到 Cocoapods存储Specs文件的库

  • pod trunk push 推送 xxx.podspec 推送到 Cocoapods存储Specs文件的库

    pod trunk push NKTestLib.podspec
    
  • 推送成功的标识: 提示信息如下:

    Validating podspec
     -> NKTestLib (0.1.0)
    - WARN  | source: Git SSH URLs will NOT work for people behind firewalls configured to only allow HTTP, therefore HTTPS is preferred.
    - WARN  | description: The description is shorter than the summary.
    - NOTE  | [NKTestLib/CommerceUI] xcodebuild:  note: Using codesigning identity override: -
    - NOTE  | [NKTestLib/CommerceUI] xcodebuild:  note: Building targets in dependency order
    - NOTE  | [iOS] xcodebuild:  NKTestLib/Assets/NKTempViewController.xib:WGN-9d-qT2: warning: Filled Button Style before iOS 15.0 [5]
    Updating spec repo `trunk`
    --------------------------------------------------------------------------------
     🎉  Congrats
    
     🚀  NKTestLib (0.1.0) successfully published
     📅  March 14th, 03:05
     🌎  https://cocoapods.org/pods/NKTestLib
     👍  Tell your friends!
    --------------------------------------------------------------------------------
    

    image.png

  • 审核
    此时你的 xxx.podspec 就会 pull request 到远程索引库,CocoaPods 官方审核通过后,就可以出现在远程索引库中,当远程索引库收录后:

    pod setup
    
  • 删除本地索引文件
    这时你的本地索引库,会新加入 你的xxx.podspec 这条记录,但是本地索引文件还未更新,因此删除掉以下路径的本地索引文件:

    ~/Library/Caches/CocoaPods/search_index.json
    
  • pod search
    执行 pod search NKTestLib 命令,当 search_index.json 文件重建完毕后,就可以在使用这个远程框架库了。

  • pod install --repo-update 导入公有云的库 image.png image.png

5.7 CocoaPods 远程库(私有库)

创建公有库远程库

远程私有库工作流程
远程私有库

5.7.1 SSH 授权

此次创建私有库,我使用的git平台是 码云
添加私有索引库需要使用 SSH 授权,也是和 Git 仓库一样的,了解的同学可以跳过这一步骤,首先创建公钥:

ssh-keygen

然后找到下面的文件:

~/.ssh/id_rsa.pub

里面存放的字符就是公钥了,然后将公钥添加码市,链接如下:

https://gitee.com/profile/sshkeys

5.7.2 添加私有远程索引库

  • 添加 pod repo库 用于存储私有库的 Spec文件:
  • 执行 pod repo,可以看到当前本地的repo库个数和相关的信息下面的信息:
    ## 输入终端指令:
    pod repo
    ## 打印:
    brightcove-brightcovespecs
    - Type: git (master)
    - URL:  https://github.com/brightcove/BrightcoveSpecs.git
    - Path: /Users/VZha33/.cocoapods/repos/brightcove-brightcovespecs
    
    nike-internal-mpe.tool.specs
    - Type: git (main)
    - URL:  git@github.com:nike-internal/mpe.tool.specs.git
    - Path: /Users/VZha33/.cocoapods/repos/nike-internal-mpe.tool.specs
    
    trunk
    - Type: CDN
    - URL:  https://cdn.cocoapods.org/
    - Path: /Users/VZha33/.cocoapods/repos/trunk
    
  • 执行 pod repo add 添加 存储我们私有库的 Specsrepo
        ## 语法:
        ## pod repo add 期望保存在本地的Repo名称 Pod私有库存储Specs的Repo git地址(一般直接用ssh授权的地址)
        ## 示例:
        pod repo add NKMobilePaaSSpecs git@gitee.com:LuckyVan_admin/nkmobile-paa-sspecs.git
    
  • 再次执行 pod repo,检查:
      ## 输入终端指令:
      pod repo
      ## 打印:
    
      brightcove-brightcovespecs
      - Type: git (master)
      - URL:  https://github.com/brightcove/BrightcoveSpecs.git
      - Path: /Users/VZha33/.cocoapods/repos/brightcove-brightcovespecs
    
      nike-internal-mpe.tool.specs
      - Type: git (main)
      - URL:  git@github.com:nike-internal/mpe.tool.specs.git
      - Path: /Users/VZha33/.cocoapods/repos/nike-internal-mpe.tool.specs
    
      NKMobilePaaSSpecs
      - Type: git (master)
      - URL:  git@gitee.com:LuckyVan_admin/nkmobile-paa-sspecs.git
      - Path: /Users/VZha33/.cocoapods/repos/NKMobilePaaSSpecs
    
      trunk
      - Type: CDN
      - URL:  https://cdn.cocoapods.org/
      - Path: /Users/VZha33/.cocoapods/repos/trunk
    
      4 repos
    
    此时我们的 CocoaPods 远程索引库就安装好了。我们也可以到下面的路径去看一下:
    ~/.cocoapods/repos
    
    image.png

5.7.3 上传源码到 Git

为了快速完成实践,我们直接把前面创建的本地库,修改一下名称和配置,上传到私有git仓,进行实践:

  • 修改文件夹名称(NKTestLib->NKTestLibPrivacyLib)
  • 修改podspec名称(NKTestLib->NKTestLibPrivacyLib)
  • 修改podspec配置
    • 名称:NKTestLib->NKTestLibPrivacyLib
    • git仓库地址:换位私有git仓地址
    • 其余保留原样
  • 修改后的podspec:
#
#  Be sure to run `pod spec lint NKTestLib.podspec' to ensure this is a
#  valid spec and to remove all comments including this before submitting the spec.
#
#  To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html
#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
#

Pod::Spec.new do |spec|

  # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  These will help people to find your library, and whilst it
  #  can feel like a chore to fill in it's definitely to your advantage. The
  #  summary should be tweet-length, and the description more in depth.
  #

  spec.name         = "NKTestLibPrivacyLib"
  spec.version      = "0.1.0"
  spec.summary      = "This is a Pod Lib Demo for blog."

  # 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!
  spec.description  = "一般描述库的用途、主要功能等"

  spec.homepage     = "https://github.com/VanZhang-CN-SZ-SN9037/NKTestLib"
  # spec.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"


  # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Licensing your code is important. See https://choosealicense.com for more info.
  #  CocoaPods will detect a license file if there is a named LICENSE*
  #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
  #

  spec.license      = { :type => "MIT", :file => "./LICENSE" }


  # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the authors of the library, with email addresses. Email addresses
  #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
  #  accepts just a name if you'd rather not provide an email address.
  #
  #  Specify a social_media_url where others can refer to, for example a twitter
  #  profile URL.
  #

  spec.author             = { "Van Zhang" => "toBeABetterManZxh91@gmail.com" }
  # Or just: spec.author    = "Van Zhang"
  # spec.authors            = { "Van Zhang" => "Van.Zhang@nike.com" }
  # spec.social_media_url   = "https://twitter.com/Van Zhang"

  # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If this Pod runs only on iOS or OS X, then specify the platform and
  #  the deployment target. You can optionally include the target after the platform.
  #

   spec.platform     = :ios, "14.0"
  # spec.platform     = :ios, "5.0"

  #  When using multiple platforms
  spec.ios.deployment_target = "14.0"
  # spec.osx.deployment_target = "10.7"
  # spec.watchos.deployment_target = "2.0"
  # spec.tvos.deployment_target = "9.0"
  spec.swift_version = "5.4"


  # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Specify the location from where the source should be retrieved.
  #  Supports git, hg, bzr, svn and HTTP.
  #

  spec.source       = { :git => "git@gitee.com:LuckyVan_admin/nktest-lib-privacy-lib.git", :tag => "#{spec.version}" }


  # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  CocoaPods is smart about how it includes source code. For source files
  #  giving a folder will include any swift, h, m, mm, c & cpp files.
  #  For header files it will include any header in the folder.
  #  Not including the public_header_files will make all headers public.
  #

  #spec.source_files  = "Classes", ".//Classes/*.{c,h,m,mm,swift}"
  #spec.exclude_files = "Classes/Exclude"
  spec.prefix_header_contents = '#import <UIKit/UIKit.h>'


  # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  A list of resources included with the Pod. These are copied into the
  #  target bundle with a build phase script. Anything else will be cleaned.
  #  You can preserve files from being cleaned, please don't preserve
  #  non-essential files like tests, examples and documentation.
  #

  # spec.resource  = "icon.png"
  # spec.resources = "Sources/Assets/*"

  # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"


  # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  Link your library with frameworks, or libraries. Libraries do not include
  #  the lib prefix of their name.
  #

  # spec.framework  = "SomeFramework"
  spec.frameworks = ['CoreTelephony','CoreFoundation','AdSupport','AppTrackingTransparency']


  # spec.library   = "iconv"
  spec.libraries = ['c++', 'resolv.9']


  # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
  #
  #  If your library depends on compiler flags you can set them in the xcconfig hash
  #  where they will only apply to your library. If you depend on other Podspecs
  #  you can include multiple dependencies to ensure it works.

  # spec.requires_arc = true

  # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }

  spec.static_framework = true 
  #spec.vendored_frameworks = 'Sources/Classes/Vendor/AliTigerTally_IDFA.xcframework'
  #spec.dependency "Alamofire", "~>5"

  # ――― Sub Spec Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #  
  spec.subspec 'CommerceUI' do |c|
    c.source_files = 'Sources/Classes/CommerceUI/**/*'
  end
  
end

  • 使用 pod lib create + 模板url,快速创建一个带 集成Demo的模板工程
        pod lib create  NKTestLibPrivacyLib Swift --template-url="https://github.com/VanZhang-CN-SZ-SN9037/pod-template"
    
  • 源码拖到 Sources/Classes 的目录: image.png
  • 图片的资源文件拖到 Sources/Assets 的目录: image.png
  • 创建私有git仓: image.png
  • 提交git日志,打tag,上传代码(注意Tag的版本号,要和Podspec里面的版本号保持一致)
    git tag '0.1.0'
    git push --tags
    

5.7.4 上传 Spec 到远程索引库

检验通过后,提示如下:

image.png

  • podspec 文件推到前面创建的远程私有索引库(NKMobilePaaSSpecs):
    pod repo push NKMobilePaaSSpecs NKTestLibPrivacyLib.podspec --allow-warnings
    
  • 推送成功的标志: image.png
  • 更新本地的 NKMobilePaaSSpecsrepo
        pod repo update NKMobilePaaSSpecs
    
  • 更新成功后,修改Podfile文件
# Uncomment the next line to define a global platform for your project
source "git@gitee.com:LuckyVan_admin/nkmobile-paa-sspecs.git"

platform :ios, '14.0'
 
target 'NKTestLibPrivacyLib' do

#   pod 'NKTestLibPrivacyLib', :path => '/Users/VZha33/GitHub/Temp/NKTestLibPrivacyLib/NKTestLibPrivacyLib.podspec',:subspecs => ['CommerceUI']

   pod 'NKTestLibPrivacyLib'

end
  • 执行 pod install,整个远程私有库的搭建和使用就完成了 image.png
  • 我们前面在创建本地库的时候,单独配置库与库的资源文件信息,也可以把库集成到其它工程中进行使用:
  • 我们此次在创建远程库的时候,使用了Cocoapods管理+个人Pod库模板+私有git仓的模式。这样做的好处:
    • 使用Cocoapods的模板库创建模式,同时创建了一个库文件包+Example工程,
      • 单独测试:可以对本库单独集成,进行功能测试、单元测试
    • 使用了个人Pod库模板而不是官方模板,避免了繁琐的创建流程
      • 使用官方模板的创建指令:
        pod lib create XXX你的库名
        ////////////////////////////////////////////////////////////////////////
        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
        
      • 使用个人模板的创建指令:
        OC库工程: pod lib create  XXX你的库名 Objc --template-url="https://github.com/VanZhang-CN-SZ-SN9037/pod-template"
        Swift库工程:pod lib create  XXX你的库名 Swift --template-url="https://github.com/VanZhang-CN-SZ-SN9037/pod-template"
        

5.8 Pod库的其它管理

5.8.1 Pods库 迭代升级

我们在搭建公司的移动开发套件的时候,随着业务的丰富以及项目功能版本的迭代,总会有对组件升级的时候。

我们现在来尝试一下,Cocoapods组件库升级的管理:
现在我们沿着前面的测试例子,给 NKTestLibPrivacyLib 进行升级,给它再增加一些功能:

  • 升级前的podspec(subspec只有CommerceUI一个子仓):
    #
    #  Be sure to run `pod spec lint NKTestLib.podspec' to ensure this is a
    #  valid spec and to remove all comments including this before submitting the spec.
    #
    #  To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html
    #  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
    #
    
    Pod::Spec.new do |spec|
    
      # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  These will help people to find your library, and whilst it
      #  can feel like a chore to fill in it's definitely to your advantage. The
      #  summary should be tweet-length, and the description more in depth.
      #
    
      spec.name         = "NKTestLibPrivacyLib"
      spec.version      = "0.1.0"
      spec.summary      = "This is a Pod Lib Demo for blog."
    
      # 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!
      spec.description  = "一般描述库的用途、主要功能等"
    
      spec.homepage     = "https://github.com/VanZhang-CN-SZ-SN9037/NKTestLib"
      # spec.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
    
    
      # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Licensing your code is important. See https://choosealicense.com for more info.
      #  CocoaPods will detect a license file if there is a named LICENSE*
      #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
      #
    
      spec.license      = { :type => "MIT", :file => "./LICENSE" }
    
    
      # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Specify the authors of the library, with email addresses. Email addresses
      #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
      #  accepts just a name if you'd rather not provide an email address.
      #
      #  Specify a social_media_url where others can refer to, for example a twitter
      #  profile URL.
      #
    
      spec.author             = { "Van Zhang" => "toBeABetterManZxh91@gmail.com" }
      # Or just: spec.author    = "Van Zhang"
      # spec.authors            = { "Van Zhang" => "Van.Zhang@nike.com" }
      # spec.social_media_url   = "https://twitter.com/Van Zhang"
    
      # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  If this Pod runs only on iOS or OS X, then specify the platform and
      #  the deployment target. You can optionally include the target after the platform.
      #
    
       spec.platform     = :ios, "14.0"
      # spec.platform     = :ios, "5.0"
    
      #  When using multiple platforms
      spec.ios.deployment_target = "14.0"
      # spec.osx.deployment_target = "10.7"
      # spec.watchos.deployment_target = "2.0"
      # spec.tvos.deployment_target = "9.0"
      spec.swift_version = "5.4"
    
    
      # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Specify the location from where the source should be retrieved.
      #  Supports git, hg, bzr, svn and HTTP.
      #
    
      spec.source       = { :git => "git@gitee.com:LuckyVan_admin/nktest-lib-privacy-lib.git", :tag => "#{spec.version}" }
    
    
      # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  CocoaPods is smart about how it includes source code. For source files
      #  giving a folder will include any swift, h, m, mm, c & cpp files.
      #  For header files it will include any header in the folder.
      #  Not including the public_header_files will make all headers public.
      #
    
      #spec.source_files  = "Classes", ".//Classes/*.{c,h,m,mm,swift}"
      #spec.exclude_files = "Classes/Exclude"
      spec.prefix_header_contents = '#import <UIKit/UIKit.h>'
    
    
      # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  A list of resources included with the Pod. These are copied into the
      #  target bundle with a build phase script. Anything else will be cleaned.
      #  You can preserve files from being cleaned, please don't preserve
      #  non-essential files like tests, examples and documentation.
      #
    
      # spec.resource  = "icon.png"
      # spec.resources = "Sources/Assets/*"
    
      # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
    
    
      # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Link your library with frameworks, or libraries. Libraries do not include
      #  the lib prefix of their name.
      #
    
      # spec.framework  = "SomeFramework"
      spec.frameworks = ['CoreTelephony','CoreFoundation','AdSupport','AppTrackingTransparency']
    
    
      # spec.library   = "iconv"
      spec.libraries = ['c++', 'resolv.9']
    
    
      # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  If your library depends on compiler flags you can set them in the xcconfig hash
      #  where they will only apply to your library. If you depend on other Podspecs
      #  you can include multiple dependencies to ensure it works.
    
      # spec.requires_arc = true
    
      # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
    
      spec.static_framework = true 
      #spec.vendored_frameworks = 'Sources/Classes/Vendor/AliTigerTally_IDFA.xcframework'
      #spec.dependency "Alamofire", "~>5"
    
      # ――― Sub Spec Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      spec.subspec 'CommerceUI' do |c|
        c.source_files = 'Sources/Classes/CommerceUI/**/*'
      end
    
    end
    
    
    image.png
  • 升级后的podspec(subspec加了Extension一个子仓):
    #
    #  Be sure to run `pod spec lint NKTestLib.podspec' to ensure this is a
    #  valid spec and to remove all comments including this before submitting the spec.
    #
    #  To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html
    #  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
    #
    
    Pod::Spec.new do |spec|
      # ―――  Spec Metadata  ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  These will help people to find your library, and whilst it
      #  can feel like a chore to fill in it's definitely to your advantage. The
      #  summary should be tweet-length, and the description more in depth.
      #
    
      spec.name         = "NKTestLibPrivacyLib"
      spec.version      = "0.1.0"
      spec.summary      = "This is a Pod Lib Demo for blog."
    
      # 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!
      spec.description  = "一般描述库的用途、主要功能等"
      spec.homepage     = "https://github.com/VanZhang-CN-SZ-SN9037/NKTestLib"
      # spec.screenshots  = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif"
    
      # ―――  Spec License  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Licensing your code is important. See https://choosealicense.com for more info.
      #  CocoaPods will detect a license file if there is a named LICENSE*
      #  Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'.
      #
    
      spec.license      = { :type => "MIT", :file => "./LICENSE" }
      # ――― Author Metadata  ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Specify the authors of the library, with email addresses. Email addresses
      #  of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
      #  accepts just a name if you'd rather not provide an email address.
      #
      #  Specify a social_media_url where others can refer to, for example a twitter
      #  profile URL.
      #
    
      spec.author             = { "Van Zhang" => "toBeABetterManZxh91@gmail.com" }
      # Or just: spec.author    = "Van Zhang"
      # spec.authors            = { "Van Zhang" => "Van.Zhang@nike.com" }
      # spec.social_media_url   = "https://twitter.com/Van Zhang"
      # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  If this Pod runs only on iOS or OS X, then specify the platform and
      #  the deployment target. You can optionally include the target after the platform.
      #
       spec.platform     = :ios, "14.0"
      # spec.platform     = :ios, "5.0"
      #  When using multiple platforms
    
      spec.ios.deployment_target = "14.0"
      # spec.osx.deployment_target = "10.7"
      # spec.watchos.deployment_target = "2.0"
      # spec.tvos.deployment_target = "9.0"
      spec.swift_version = "5.4"
      # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Specify the location from where the source should be retrieved.
      #  Supports git, hg, bzr, svn and HTTP.
      #
      spec.source       = { :git => "git@gitee.com:LuckyVan_admin/nktest-lib-privacy-lib.git", :tag => "#{spec.version}" }
    
      # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  CocoaPods is smart about how it includes source code. For source files
      #  giving a folder will include any swift, h, m, mm, c & cpp files.
      #  For header files it will include any header in the folder.
      #  Not including the public_header_files will make all headers public.
      #
      #spec.source_files  = "Classes", ".//Classes/*.{c,h,m,mm,swift}"
      #spec.exclude_files = "Classes/Exclude"
      spec.prefix_header_contents = '#import <UIKit/UIKit.h>'
      # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  A list of resources included with the Pod. These are copied into the
      #  target bundle with a build phase script. Anything else will be cleaned.
      #  You can preserve files from being cleaned, please don't preserve
      #  non-essential files like tests, examples and documentation.
      #
      # spec.resource  = "icon.png"
      # spec.resources = "Sources/Assets/*"
      # spec.preserve_paths = "FilesToSave", "MoreFilesToSave"
    
      # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  Link your library with frameworks, or libraries. Libraries do not include
      #  the lib prefix of their name.
      #
      # spec.framework  = "SomeFramework"
      spec.frameworks = ['CoreTelephony','CoreFoundation','AdSupport','AppTrackingTransparency']
      # spec.library   = "iconv"
      spec.libraries = ['c++', 'resolv.9']
    
      # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
      #
      #  If your library depends on compiler flags you can set them in the xcconfig hash
      #  where they will only apply to your library. If you depend on other Podspecs
      #  you can include multiple dependencies to ensure it works.
      # spec.requires_arc = true
      # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
      spec.static_framework = true 
      #spec.vendored_frameworks = 'Sources/Classes/Vendor/AliTigerTally_IDFA.xcframework'
    
      #spec.dependency "Alamofire", "~>5"
      
      # ――― Sub Spec Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
        spec.subspec 'Extension' do |e|
           e.source_files = 'Sources/Classes/Extension/**/*'
        end
      spec.subspec 'CommerceUI' do |c|
    
        c.source_files = 'Sources/Classes/CommerceUI/**/*'
    
      end 
    end
    
    image.png
  • 检查一下私有库spec是否有错误:
    pod lib lint --allow-warnings
    
    image.png
  • 检查通过后,修改podspec中的库版本号为0.2.0,提交本地git日志,打就可以将 NKTestLibPrivacyLib0.2.0 版本推到远程私有库中,同时建立 0.2.0 的 Tag
  • 推送新版本的podspec到私有库Spec文件的存储仓NKMobilePaaSSpecs 检查通过后,执行:
    pod repo push NKMobilePaaSSpecs NKTestLibPrivacyLib.podspec --allow-warnings
    
    image.png
  • 更新本地的 NKMobilePaaSSpecs
    pod repo update NKMobilePaaSSpecs
    
    image.png
  • 重新 在 工程中 pod install, 集成新版本的 NKTestLibPrivacyLib: image.png image.png

5.8.2 Pod库 资源管理

我们在 创建 pod库的时候注意设置好资源文件路径的管理规则,再加载Pod库的资源存储的bundle路径+文件名称即可正常加载资源。
现在来演示一遍:

  • 检查.podspec对资源文件管理的设置:
    ## 采用 resource_bundles 字段管理,会默认给pod库创造一个指定名称的bundle文件,将指定的资源全部存储在bundle里面,在集成工程完成Build的时候,会把该bundle存储在集成工程内部
    ## 此处 案例中 NKTestLibPrivacyLibResources 为 bundle的名称
    ## => 只想的括号内部,分别是资源文件在原本的Pod库中的存储路径
    spec.resource_bundles    = {
         'NKTestLibPrivacyLibResources' => ['Sources/Classes/**/*.xib', 'Sources/Assets/*']
    }
    
  • command+B,build一下项目,找到工程内部的Product文件夹,show in finder,检查一下生成的bundle的结果: image.png image.png image.png image.png image.png
  • 我写了一个Extension帮助我们快速获取资源文件的路径,仅供参考:
    • NSBundle+BundlePath.h
      //
      //  NSString+BundlePath.h
      //  NKTestLibPrivacyLib
      //
      //  Created by Van Zhang on 2023/3/16.
      //
      
      #import <Foundation/Foundation.h>
      NS_ASSUME_NONNULL_BEGIN
      @interface NSBundle (BundlePath)
      + (NSBundle*)currentLibraryBundle;
      + (NSBundle*)resourceBundleWith:(NSString*)bundleName;
      + (NSString*)pathWithBundleName:(NSString*)bundle;
      @end
      NS_ASSUME_NONNULL_END
      
    • NSBundle+BundlePath.m (Tips:大家若是要使用,记得针对你当前的pod库修改currentLibraryBundle内部的bundle名称(把前面.podspecspec.resource_bundles字段的bundle名称拷过来即可)
      //
      //  NSBundle+BundlePath.m
      //  NKTestLibPrivacyLib
      //
      //  Created by Van Zhang on 2023/3/16.
      //
      
      #import "NSBundle+BundlePath.h"
      
      @implementation NSBundle (BundlePath)
      
      static NSBundle *resourceBundle = nil;
      static dispatch_once_t onceToken;
      
      + (NSBundle*)currentLibraryBundle{
          dispatch_once(&onceToken, ^{
              resourceBundle = [NSBundle resourceBundleWith:@"NKTestLibPrivacyLibResources"];
          });
          return resourceBundle;
      }  
      
      + (NSString *)pathWithBundleName:(NSString *)bundle{
          NSString *bundleName = [self checkBundleNamePathExtension:bundle];
          NSString *bundlePath = nil;
          for (bundle in [NSBundle allBundles]) {
              if ([[(NSBundle*)bundle bundlePath].lastPathComponent isEqualToString:bundleName]){
                  bundlePath = [(NSBundle*)bundle bundlePath];
                  break
              }
          }
          return bundlePath;
      }
      
      + (NSBundle*)resourceBundleWith:(NSString*)bundleName{
          return [NSBundle bundleWithPath:[NSBundle pathWithBundleName:bundleName]];
      }
      + (NSString*)checkBundleNamePathExtension:(NSString*)bundleName{
          NSString *bundlePathExtension = @"bundle";
          return [bundleName stringByAppendingPathExtension:[bundleName.pathExtension isEqualToString:@"bundle"]?@"":bundlePathExtension];
      }
      @end
      
    • 我们 修改一下 工程 加载资源的 代码:
      • pod 库内部 的代码加载资源: image.png
        NSString *filePath = [[NSBundle currentLibraryBundle] pathForResource:@"images/0" ofType:@"jpg"];
        UIImage *img = [UIImage imageWithContentsOfFile:filePath];
        
      • 集成pod库的项目内部 的代码加载资源: image.png
        let topBundlePath = Bundle.main.path(forResource: "NKTestLibPrivacyLibResources", ofType: "bundle")
        
        guard let nibBundlePath = topBundlePath else {
            return
        }
        
        let topBundle = Bundle(path: nibBundlePath)
        let tempVc = NKTempViewController(nibName: "NKTempViewController", bundle: topBundle)
        
        self.present(tempVc, animated: true) {
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3, execute: DispatchWorkItem(block: {
                tempVc.dismiss(animated: true)
            }))
        }
        // print((UIImage(contentsOfBundleName: "NKTestLibPrivacyLib", file: "images/0.jpg")) ?? "没有图片")
        print("bundlePath:\(Bundle.currentLibrary())")
        
    • run 一下工程 检查一下 加载 效果: image.png image.png image.png image.png

至此,当前针对Cocoapods的资源管理调整与实践基本完成

5.9 Podfile文件的优化

我们可以在Podfile中加入这一段代码:

install! 'cocoapods',
      :deterministic_uuids => false,
      :lock_pod_sources => false,
      :disable_input_output_paths => true,
      :generate_multiple_pod_projects => true,
      :preserve_pod_file_structure => true

inhibit_all_warnings!

post_install do | installer |
      installer.pods_project.targets.each do |target|
         target.build_configurations.each do |config|
            puts target.name
         end
      end

     aggregate_target = installer.aggregate_targets[0]
     aggregate_build_settings_by_config = aggregate_target.xcconfigs.collect { |k, v| [k, v.to_hash] }.to_h
     puts aggregate_build_settings_by_config
     installer.pod_target_subprojects.each do |pod_subproject|
       pod_subproject.native_targets.each do |pod_native_target|
         puts pod_native_target
         pod_native_target.build_configurations.each do |build_configuration|
             aggregate_build_settings = aggregate_build_settings_by_config[build_configuration.name]
             build_configuration.build_settings['HEADER_SEARCH_PATHS'] = "${PODS_ROOT}/Headers/Public"
             build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] = aggregate_build_settings['FRAMEWORK_SEARCH_PATHS']
             build_configuration.build_settings['LIBRARY_SEARCH_PATHS'] = aggregate_build_settings['LIBRARY_SEARCH_PATHS']
         end
       end
     end
end

这段代码的功能是把每一个pod库在导入的时候,都编译成一个静态库Framework,在打包项目包体积的同时,还可以适当提高平时开发的编译速度。

  • 添加之前的Podfile
    # Uncomment the next line to define a global platform for your project
    
    source "git@gitee.com:LuckyVan_admin/nkmobile-paa-sspecs.git"
    
    #use_frameworks! :linkage => :static
    #use_modular_headers!
    
    platform :ios, '14.0'
    
    target 'NKTestLibPrivacyLib-Example' do
        pod 'NKTestLibPrivacyLib', :path => '/Users/VZha33/GitHub/Temp/NKTestLibPrivacyLib/NKTestLibPrivacyLib.podspec'
    
    #   ,:subspecs => ['CommerceUI']
    #   pod 'NKTestLibPrivacyLib'
    end
    
  • 添加之后的Podfile
       # Uncomment the next line to define a global platform for your project
    
       source "git@gitee.com:LuckyVan_admin/nkmobile-paa-sspecs.git"
    
       #use_frameworks! :linkage => :static
       #use_modular_headers!
    
       platform :ios, '14.0'
    
       install! 'cocoapods',
             :deterministic_uuids => false,
             :lock_pod_sources => false,
             :disable_input_output_paths => true,
             :generate_multiple_pod_projects => true,
             :preserve_pod_file_structure => true
    
       inhibit_all_warnings!
    
       post_install do | installer |
             installer.pods_project.targets.each do |target|
                target.build_configurations.each do |config|
                   puts target.name
                end
             end
    
            aggregate_target = installer.aggregate_targets[0]
            aggregate_build_settings_by_config = aggregate_target.xcconfigs.collect { |k, v| [k, v.to_hash] }.to_h
            puts aggregate_build_settings_by_config
            installer.pod_target_subprojects.each do |pod_subproject|
              pod_subproject.native_targets.each do |pod_native_target|
                puts pod_native_target
                pod_native_target.build_configurations.each do |build_configuration|
                    aggregate_build_settings = aggregate_build_settings_by_config[build_configuration.name]
                    build_configuration.build_settings['HEADER_SEARCH_PATHS'] = "${PODS_ROOT}/Headers/Public"
                    build_configuration.build_settings['FRAMEWORK_SEARCH_PATHS'] = aggregate_build_settings['FRAMEWORK_SEARCH_PATHS']
                    build_configuration.build_settings['LIBRARY_SEARCH_PATHS'] = aggregate_build_settings['LIBRARY_SEARCH_PATHS']
                end
              end
            end
       end
    
       target 'NKTestLibPrivacyLib-Example' do
           pod 'NKTestLibPrivacyLib', :path => '/Users/VZha33/GitHub/Temp/NKTestLibPrivacyLib/NKTestLibPrivacyLib.podspec'
    
       #   ,:subspecs => ['CommerceUI']
       #   pod 'NKTestLibPrivacyLib'
       end
    
  • 添加之前的工程目录 image.png
  • 添加之后的工程目录 image.png

私有库实践工程的链接:github.com/VanZhang-CN…

六、 CocoaPods跳过验证

我们在使用私有组件库的时候,有时候会遇到这个问题,podspec明明可以正常使用,:path和:git方式使用都正常,但是pod lib lint和pod spec lint时报些诡异的Error出来?

或者已经确定了podspec文件没有问题,不想在验证的环节上浪费时间?这时候我们可以通过这个方法直接跳过验证进行强推(提醒:建议有时间的话还是按正常流程来操作)

  • 查看pod路径:
    gem which cocoapods
    
  • 终端显示
    /Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods.rb
    
  • 从路径里截取lib路径:
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib
  • 拼接上验证器路径:
/cocoapods/validator.rb
  • 得到验证器完整路径:
/Library/Ruby/Gems/2.6.0/gems/cocoapods-1.10.1/lib/cocoapods/validator.rb
  • 打开这个文件,搜索:
def xcodebuild(
  • 找到这段代码:

image.png

  • 替换成:
command += %w(-version)

然后就可以顺利通过pod lib lint和pod spec lint,直接pod trunk push了。

总结

我们本次在本篇文章依次介绍了依赖包管理工具的:

  • 发明背景和用途
  • Pod库的分类:本地库、主库+子库、远程公有库、远程私有库
  • Pod库的创建和相关的资源管理:.podspec的配置、pod库资源管理、pod远程库发布和Pod库升级
  • ...

本次并未拓展讨论的话题

  • 深入讨论 pod库个人模板工程的定制 以及 相关 脚本文件的书写
  • Cocoapods 插件开发
  • Cocoapods + Fastlane + Jenkins 实现iOS工程的持续集成
  • Cocoapods 底层原理
  • 推荐的Cocoapods优质开源库
    • 若是你毫无思路,欢迎阅读我的这篇文章(建立自己的iOS知识体系),先了解一下iOS开发的过程中,各个阶段需要关注什么,从而选择那些三方库/朝着哪些方向去制作自己的Pod库以实现自己公司内部的iOS移动开发套件

若我们要进一步对未讨论的子话题挖掘,我们则需要掌握更多的前知识。
比如,pod库个人模板工程的定制 以及 相关 脚本文件的书写、Cocoapods 插件开发、Cocoapods + Fastlane + Jenkins 实现iOS工程的持续集成、Cocoapods 底层原理等话题。则要求我们需要掌握一定的脚本语言Ruby、Fastlane、Gem、Homebrew、Jenkins等
若是你有兴趣,也可以和我分享你的探索经验。欢迎Q我,让我们相互借鉴,共同进步!!! ......

相关阅读

专题系列文章

iOS架构设计

探索iOS底层原理