Wrappres' Studio.

CocoaPods相关

字数统计: 2.3k阅读时长: 9 min
2021/06/09 Share

摘自这里

Version Control System (VCS)

Version control systems are a category of software tools that help a software team manage changes to source code over time. Version control software keeps track of every modification to the code in a special kind of database.

软件工程中,版本控制系统是敏捷开发的重要一环,为后续的持续集成提供了保障。Source Code Manager (SCM) 源码管理就属于 VCS 的范围之中,熟知的工具有如 Git 。而 CocoaPods 这种针对各种语言所提供的 Package Manger (PM)也可以看作是 SCM 的一种。

而像 GitSVN 是针对项目的单个文件的进行版本控制,而 PM 则是以每个独立的 Package 作为最小的管理单元。包管理工具都是结合 SCM 来完成管理工作,对于被 PM 接管的依赖库的文件,通常会在 Git.ignore 文件中选择忽略它们。

Git Submodule

Git submodules allow you to keep a git repository as a subdirectory of another git repository. Git submodules are simply a reference to another repository at a particular snapshot in time. Git submodules enable a Git repository to incorporate and track version history of external code.

Git Submodules 可以算是 PM 的“青春版”,它将单独的 git 仓库以子目录的形式嵌入在工作目录中。它不具备 PM 工具所特有的语义化版本管理、无法处理依赖共享与冲突等。只能保存每个依赖仓库的文件状态。

Git 在提交更新时,会对所有文件制作一个快照并将其存在数据库中。Git 管理的文件存在 3 种状态:

  • working director: 工作目录,即我们肉眼可见的文件
  • stage area: 暂存区 (或称 index area ),存在 .git/index 目录下,保存的是执行 git add 相关命令后从工作目录添加的文件。
  • commit history: 提交历史,存在 .git/ 目录下,到这个状态的文件改动算是入库成功,基本不会丢失了。

Git submodule 是依赖 .gitmodules 文件来记录子模块的。

1
2
3
[submodule "ReactNative"]
path = ReactNative
url = https://github.com/facebook/ReactNative.git

.gitmodules 仅记录了 path 和 url 以及模块名称的基本信息, 但是我们还需要记录每个 Submodule Repo 的 commit 信息,而这 commit 信息是记录在 .git/modules 目录下。同时被添加到 .gitmodules 中的 path 也会被 git 直接 ignore 掉。

Package Manger

作为 Git Submodule 的强化版,PM 基本都具备了语义化的版本检查能力,依赖递归查找,依赖冲突解决,以及针对具体依赖的构建能力和二进制包等。简单对比如下:
Key File | Git submodule | CocoaPods | SPM | npm
:-: | :-: | :-: | :-: | :-:
描述文件 | .gitmodules | Podfile | Package.swift | Package.json
锁存文件 | .git/modules | Podfile.lock | Package.resolved | package-lock.json

从 👆 可见,PM 工具基本围绕这个两个文件来现实包管理:

描述文件:声明了项目中存在哪些依赖,版本限制;
锁存文件(Lock 文件):记录了依赖包最后一次更新时的全版本列表。
除了这两个文件之外,中心化的 PM 一般会提供依赖包的托管服务,比如 npm 提供的 npmjs.com可以集中查找和下载 npm 包。如果是去中心化的 PM 比如 iOS 的 Carthage 和 SPM 就只能通过 Git 仓库的地址了。

CocoaPods

Podfile

Podfile 是一个文件,以 DSL(其实直接用了 Ruby 的语法)来描述依赖关系,用于定义项目所需要使用的第三方库。该文件支持高度定制,你可以根据个人喜好对其做出定制。

Podfile.lock

这是 CocoaPods 创建的最重要的文件之一。它记录了需要被安装的 Pod 的每个已安装的版本。如果你想知道已安装的 Pod 是哪个版本,可以查看这个文件。推荐将 Podfile.lock 文件加入到版本控制中,这有助于整个团队的一致性。

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,或者在一些不太明显的地方编译失败。

Master Specs Repo

Ultimately, the goal is to improve discoverability of, and engagement in, third party open-source libraries, by creating a more centralized ecosystem.

作为包管理工具,CocoaPods 的目标是为我们提供一个更加集中的生态系统,来提高依赖库的可发现性和参与度。本质上是为了提供更好的检索和查询功能,可惜成为了它的问题之一。因为 CocoaPods 通过官方的 Spec 仓库来管理这些注册的依赖库。随着不断新增的依赖库导致 Spec 的更新和维护成为了使用者的包袱。

好在这个问题在 1.7.2 版本中已经解决了,CocoaPods 提供了 Mater Repo CDN ,可以直接 CDN 到对应的 Pod 地址而无需在通过本地的 Spec 仓库了。同时在 1.8 版本中,官方默认的 Spec 仓库已替换为 CDN,其地址为 https://cdn.cocoapods.org。

Ruby 生态及工具链

RVM & rbenv

RVMrbenv都是管理多个 Ruby 环境的工具,它们都能提供不同版本的 Ruby 环境管理和切换。

The RubyGems software allows you to easily download, install, and use ruby software packages on your system. The software package is called a “gem” which contains a packaged Ruby application or library.

RubyGems[14] 是 Ruby 的一个包管理工具,这里面管理着用 Ruby 编写的工具或依赖我们称之为 Gem。

并且 RubyGems 还提供了 Ruby 组件的托管服务,可以集中式的查找和安装 library 和 apps。当我们使用 gem install xxx 时,会通过 rubygems.org 来查询对应的 Gem Package。而 iOS 日常中的很多工具都是 Gem 提供的,例:BundlerfastlanejazzyCocoaPods 等。

在默认情况下 Gems 总是下载 library 的最新版本,这无法确保所安装的 library 版本符合我们预期。因此我们还缺一个工具。

Bundler

Bundler[15] 是管理 Gem 依赖的工具,可以隔离不同项目中 Gem 的版本和依赖环境的差异,也是一个 Gem。

Bundler 通过读取项目中的依赖描述文件 Gemfile ,来确定各个 Gems 的版本号或者范围,来提供了稳定的应用环境。当我们使用 bundle install 它会生成 Gemfile.lock 将当前 librarys 使用的具体版本号写入其中。之后,他人再通过 bundle install 来安装 libaray 时则会读取 Gemfile.lock 中的 librarys、版本信息等。

Gemfile

可以说 CocoaPods 其实是 iOS 版的 RubyGems + Bundler 组合。Bundler 依据项目中的 Gemfile 文件来管理 Gem,而 CocoaPods 通过 Podfile 来管理 Pod。

安装一套可管控的 Ruby 工具链

homebrew + rbenv + RubyGems + Bundler

使用 homebrew 安装 rbenv

1
brew install rbenv

使用 rbenv 管理 Ruby 版本

安装ruby版本

1
rbenv install 2.7.0

让其在本地环境中生效

1
rbenv shell 2.7.0

输入上述命令后,可能会有报错。rbenv 提示我们在 .zshrc 中增加一行 eval “$(rbenv init -)” 语句来对 rbenv 环境进行初始化。如果报错,我们增加并重启终端即可。

1
2
3
4
$ ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
$ which ruby
/Users/gua/.rbenv/shims/ruby
1
2
$ which gem
/Users/bytedance/.rbenv/shims/gem

查询系统级 Gem 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ gem list

*** LOCAL GEMS ***

activesupport (4.2.11.3)
...
claide (1.0.3)
cocoapods (1.9.3)
cocoapods-core (1.9.3)
cocoapods-deintegrate (1.0.4)
cocoapods-downloader (1.3.0)
cocoapods-plugins (1.0.0)
cocoapods-search (1.0.0)
cocoapods-stats (1.1.0)
cocoapods-trunk (1.5.0)
cocoapods-try (1.2.0)

如何使用 Bundler 管理工程中的 Gem 环境

在 iOS 工程中初始化 Bundler 环境

先来初始化一个 Bundler 环境(其实就是自动创建一个 Gemfile 文件)

1
2
3
$ bundle init

Writing new Gemfile to /Users/Gua/GuaDemo/Gemfile

在 Gemfile 中声明使用的 CocoaPods 版本并安装

之后我们编辑一下这个 Gemfile 文件,加入我们当前环境中需要使用 CocoaPods 1.5.3 这个版本,则使用 Gemfile 的 DSL 编写以下内容

1
2
3
4
5
6
7
8
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"
gem "cocoapods", "1.5.3"

编写之后执行一下 bundle install

1
2
3
4
5
6
7
$ bundle install
Fetching gem metadata from https://gems.ruby-china.com/............
Resolving dependencies...
...
Fetching cocoapods 1.5.3
Installing cocoapods 1.5.3
Bundle complete! 1 Gemfile dependency, 30 gems now installed.

使用当前环境下的 CocoaPods 版本操作 iOS 工程

检查一下当前 Bundler 环境下的 Gem 列表

1
2
3
4
5
6
7
8
9
10
11
$ bundle exec gem list

*** LOCAL GEMS ***

activesupport (4.2.11.3)
atomos (0.1.3)
bundler (2.1.4)
CFPropertyList (3.0.2)
claide (1.0.3)
cocoapods (1.5.3)
...

此时我们使用 bundle exec pod install 来执行 Install 这个操作,就可以使用 CocoaPods 1.5.3 版本来执行 Pod 操作了。

1
2
3
4
5
6
7
8
$ bundle exec pod install
Analyzing dependencies
Downloading dependencies
Installing SnapKit (5.0.1)
Integrating client project
[!] Please close any current Xcode sessions and use `GuaDemo.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.

Podfile.lock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cat Podfile.lock
PODS:
- SnapKit (5.0.1)

DEPENDENCIES:
- SnapKit (~> 5.0.0)

SPEC REPOS:
https://github.com/cocoapods/specs.git:
- SnapKit

SPEC CHECKSUMS:
SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb

PODFILE CHECKSUM: 1a4b05aaf43554bc31c90f8dac5c2dc0490203e8

COCOAPODS: 1.5.3

CATALOG
  1. 1. Version Control System (VCS)
    1. 1.1. Git Submodule
    2. 1.2. Package Manger
    3. 1.3. CocoaPods
      1. 1.3.1. Podfile
      2. 1.3.2. Podfile.lock
      3. 1.3.3. Manifest.lock
      4. 1.3.4. Master Specs Repo
    4. 1.4. Ruby 生态及工具链
      1. 1.4.1. RVM & rbenv
      2. 1.4.2. Bundler
        1. 1.4.2.1. Gemfile
  2. 2. 安装一套可管控的 Ruby 工具链
    1. 2.1. 使用 homebrew 安装 rbenv
    2. 2.2. 使用 rbenv 管理 Ruby 版本
    3. 2.3. 查询系统级 Gem 依赖
    4. 2.4. 如何使用 Bundler 管理工程中的 Gem 环境
      1. 2.4.1. 在 iOS 工程中初始化 Bundler 环境
      2. 2.4.2. 在 Gemfile 中声明使用的 CocoaPods 版本并安装
      3. 2.4.3. 使用当前环境下的 CocoaPods 版本操作 iOS 工程