CocoaPods adventuresThe topic is
Edmond 和
Wax gourdThis is a record of the implementation details, principles, source code, practice and experience of CocoaPods, a version management tool in iOS/MacOS engineering. It is designed to help you to understand more about this dependency management tool, and not only limited to
pod install 和
pod update.

Contents of knowledge of this article

background

CocoaPods as an industry standard, you should be familiar with iOS development students. However, many students’ use of CocoaPods basically stays on pod install and pod update. Once the project componentization, each line of business logic split into independent POD, light understanding of a few simple POD command is unable to meet the needs, but also faced with the consistency of the development environment, POD command execution in various abnormal errors, all require us to have a deeper understanding and 🤔.

There are many articles on CocoaPods in-depth, and this one from OBJC China is recommended for an in-depth understanding of CocoaPods, but this article wants to talk about the management philosophy of CocoaPods from a dependency management tool perspective.

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.

In software engineering, version control system is an important part of agile development, which provides a guarantee for continuous integration. Source Code Manager (SCM) Source management falls within the purview of VCS, with tools such as Git as well known. The Package Manger (PM) provided by CocoaPods for various languages can also be regarded as a kind of SCM.

Whereas Git or SVN is versioned for a single file of a project, PM is versioned for each individual Package as the smallest snap-in. Package-management tools combine SCM to do their job. Files that depend on libraries that PM takes over will usually be ignored in Git’s.ignore file.

For example, in a Node project, the files in the node_modules directory are ignored, and in an iOS/MacOS project, they are Pods.

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 can be a “young version” of PM, embedding a separate Git repository in a working directory as a subdirectory. It does not have the semantic versioning, dependency sharing, and conflict features of PM tools. Only the file state of each dependent repository can be saved.

When Git commits an update, it takes a snapshot of all files and stores them in the database. Git manages files in three states:

  • Working Director: A file that can be seen by the human eye
  • Stage area:Temporary storage areaindex area),.git/indexDirectory, save is the executiongit addFiles added from the working directory after the relevant command.
  • Commit the history:Submit history, exist.git/Directory, to this state of the file changes are considered successful in the library, basically will not be lost.

Git submodules rely on the.gitmodules file to keep track of their children.

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

.gitModules only records the basic information of the path, URL and module name, but we also need to record the commit information of each Submodule Repo, which is recorded in the.git/modules directory. A path added to. GitModules will also be ignored by Git.

Package Manger

As an enhanced version of Git Submodule, PM basically has the ability of semantic version checking, dependency recursive lookup, dependency conflict resolution, as well as the ability to build specific dependencies and binary packages. Simple comparison is as follows:

Key File Git submodule CocoaPods SPM npm
Description file .gitmodules Podfile Package.swift Package.json
Latches file .git/modules Podfile.lock Package.resolved package-lock.json

As you can see from 👆, the PM tool basically manages packages around these two files:

  • Description file: Declare which dependencies and version restrictions exist in the project;
  • Lock files: Record the full version list of dependent packages when they were last updated.

In addition to these two files, centralized PMs typically provide package-dependent hosting services, such as npmjs.com, which provides a centralized way to find and download NPM packages. If it is a decentralized PM such as iOS Carthage and SPM, it can only be accessed through the Git repository address.

CocoaPods

CocoaPods is a dependency management tool for a third-party library for developing iOS/ MacOS applications. With CocoaPods, you can define your own dependencies (PODS for short) and versioning third-party libraries throughout your development environment.

Let’s take CocoaPods as an example.

Podfile

Podfile is a file that describes the dependencies in a DSL (actually directly Ruby syntax) and defines the third-party libraries that your project will need to use. The file supports a high degree of customization, and you can customize it to your liking. For more information, check out the Podfile Guide.

Podfile.lock

This is one of the most important files CocoaPods creates. It records each installed version of the POD that needs to be installed. If you want to know which version of POD you have installed, you can check this file. It is recommended that you add the podfile.lock file to version control to help maintain consistency across the team.

Manifest.lock

This is a copy of the file podfile.lock that is created every time the pod install command is run. If you’ve ever encountered an error where The sandbox file is not in sync with The podfile.lock file, This is caused by inconsistencies between the MANIFEST.LOCK file and the PODFILE.LOCK file. Since the Pods directory is not always under version control, this ensures that developers will be able to update their Pods before running their App, otherwise the App will crash or fail to compile in some less obvious place.

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.

As a package management tool, the goal of CocoaPods is to provide us with a more centralized ecosystem to increase discoverability and engagement with dependent libraries. It is essentially to provide better retrieval and query capabilities, but it is one of its problems. Because CocoaPods manages these registered dependency libraries through the official SPEC repository. Updating and maintenance of specs becomes a burden for users as a result of the increasing number of dependent libraries.

Fortunately, this issue has been resolved since version 1.7.2. CocoaPods provides a Mater Repo CDN that can be CDN directly to the corresponding Pod address without having to go through the local Spec repository. In version 1.8 at the same time, the CDN is the official Spec warehouse has been replaced by default, the address is https://cdn.cocoapods.org.

Ruby ecology and toolchains

For some students who only have contact with CocoaPods, their PMs may not be familiar with it. CocoaPods borrows its ideas from other languages’ PM tools, such as RubyGems, Bundler, NPM, and Gradle.

We know CocoaPods is implemented in Ruby. It’s a Gem package in itself. Understanding Ruby dependency management helps us better manage different versions of CocoaPods and other gems. At the same time, the ability to ensure that all colleagues on the team are on the same version of the tool is a guarantee of agile development.

RVM & rbenv

RVM 和 rbenvBoth are tools for managing multiple Ruby environments, and both provide management and switching of different versions of the Ruby environment.

Which is better depends on personal habits.Of course,
rbenvThat’s what the authorities say
Why rbenv. Subsequent experiments in this paper are also used
rbenvDemonstrate.

RubyGems

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.

RubyGemsRuby is a package management tool for Ruby that manages tools written in Ruby or dependencies we call gems.

RubyGems also provides a hosted service for Ruby components that centrally locate and install libraries and apps. When we use gem install XXX, we query the corresponding gem Package via rubygems.org. Many of the everyday tools in iOS are provided by Gem, for example: Bundler, Fastlane, Jazzy, CocoaPods, etc.

By default, Gems always download the latest version of the library, which does not guarantee that the installed version of the library will be as expected. So we need one more tool.

Bundler

BundlerGem Dependency Management (GEM) is a tool for managing Gem dependencies, isolating differences between project versions and dependencies.

Bundler provides a stable application environment by reading a dependency description file, Gemfile, in the project to determine the version number or range of each gem. When we use bundle install, it will generate Gemfile.lock and write the specific version number currently used by Librarys into it. Later, when others install libaray via bundle install, they will read librarys and version information in Gemfile.lock.

Gemfile

To say theCocoaPodsIt’s a combination of RubyGems + Bundler for iOS. Bundler is based on the projectGemfileFile to manage the Gem, whileCocoaPodsManage PODS by Podfile.

Gemfile is configured as follows:

Source 'https://gems.example.com' do gem' cocoaPods ', '1.8.4' is a gem' another_gem', :git => 'https://looseyi.github.io.git', :branch => 'master' end

As you can see, Podfile’s DSL is similar to Gemfile’s. So when would Gemfile be used?

CocoaPods releases a few major updates each year. As we discussed earlier, CocoaPods will modify the project’s.xcodeproj file during the install process, which will vary from version to version. These changes can cause conflicts and handling incorrectly. The project won’t work properly. I’m sure you don’t want to fix the.xcodeproj conflict.

If the project is based on Fastlane for continuous integration work and APP packaging work, it also needs its version management and other functions.

How do I install a manageable Ruby toolchain?

Now that we’ve talked about the division of labor of these tools, we’ll talk about the actual use of these tools. We can use the complete toolchain of Homebrew + Rbenv + RubyGems + Bundler to control versioning of Ruby tools in a project.

Here is the Ruby toolchain hierarchy diagram that I think is manageable. Below we describe the management mode of each layer one by one, and the actual operation method.

1. UsehomebrewThe installationrbenv

$ brew install rbenv

After the installation is successful, type rbenv to see the prompt:

$ rbenv

rbenv 1.1.2
Usage: rbenv <command> [<args>]

Some useful rbenv commands are:
   commands    List all available rbenv commands
   local       Set or show the local application-specific Ruby version
   global      Set or show the global Ruby version
   shell       Set or show the shell-specific Ruby version
   install     Install a Ruby version using ruby-build
   uninstall   Uninstall a specific Ruby version
   rehash      Rehash rbenv shims (run this after installing executables)
   version     Show the current Ruby version and its origin
   versions    List installed Ruby versions
   which       Display the full path to an executable
   whence      List all Ruby versions that contain the given executable

See `rbenv help <command>' for information on a specific command.
For full documentation, see: https://github.com/rbenv/rbenv#readme

2. UserbenvManaging Ruby versions

To install a Ruby version, use rbenv. Here I’m using Ruby 2.7, which I just released:

$rbenv install 2.7.0

This installation process is a bit long, as it takes about 20 minutes to download the OpenSSL and Ruby interpreter.

After the installation is successful, we make it work in the local environment:

$rbenv shell 2.7.0

You may receive an error after typing the above command.
rbenvCue us at
.zshrcAdd a line to the
eval "$(rbenv init -)"Statement to the
rbenvThe environment is initialized. If an error is reported, we add and restart the terminal.

$ruby --version ruby 2.7.0p0 (2019-12-25 Revision 647EE6F091) [x86_64-darwin19] $which ruby /Users/gua/.rbenv/shims/ruby

After the switch, we find that Ruby has been switched to the admin version of Rbenv, and its startup PATH has also been changed to Ruby under Rbenv. And we can look at the PATH of gems bundled with Ruby:

$ which gem
/Users/bytedance/.rbenv/shims/gem

The corresponding Gem has also become the PATH in rbenv.

3. Query system levelGemRely on

As such, we have isolated Ruby and its Gem environment on a version basis using rbenv. We can select all gem dependencies from the current system using the gem list command:

$gem list *** LOCAL GEMS *** ActiveSupport (4.2.11.3)... Claide (1.0.3) Integrate (1.9.3) Integrate (1.0.4) Integrate (1.3.0) CocoaPods-Search (1.0.0) CocoaPods-Search (1.0.0) CocoaPods-Stats (1.1.0) CocoaPods-Trunks (1.5.0) CocoaPods-Try (1.2.0)

Keep in mind the CocoaPods version here, which we’ll query later in the project.

Now that we have completed the configuration of the Ruby/Gem environment, let’s look at the funnel chart again:

How do I use Bundler to manage the Gem environment in my project

Let’s see how we can use Bundler to lock the Gem environment in a project so that the team can unify all versions of Ruby tools in the Gem environment. To avoid file conflicts and unnecessary errors.

Below is a hierarchical diagram of the GEM environment in the project. We can add a Gemfile description to the project to lock the GEM dependent environment in the current project.

The following will also describe each layer of management, as well as the actual operation method.

1. Initialize in the iOS projectBundlerThe environment

First we have an iOS Demo project – Guademo:

$ ls -al
total 0
drwxr-xr-x   4 gua  staff   128 Jun 10 14:47 .
drwxr-xr-x@ 52 gua  staff  1664 Jun 10 14:47 ..
drwxr-xr-x   8 gua  staff   256 Jun 10 14:47 GuaDemo
drwxr-xr-x@  5 gua  staff   160 Jun 10 14:47 GuaDemo.xcodeproj

Begin by initializing a Bundler environment (which automatically creates a Gemfile) :

$ bundle init

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

In 2.GemfileDeclared inCocoaPodsVersion and install

If you need to use CocoaPods 1.5.3 in your current environment, then write the following using the Gemfile DSL:

# frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } # gem "rails" 1.5.3 gem "cocoapods","

After writing it, execute the bundle install:

$ 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 1.5.3 has been installed successfully, and a Gemfile.lock file has been saved to lock the dependencies.

3. Use the current environmentCocoaPodsVersion operating iOS project

At this point we can examine the list of gems in the current Bundler environment:

$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)...

I found that this list is much smaller than the global Gem list, and only includes basic Gem dependencies and CocoaPods Gem dependencies. You can use CocoaPods version 1.5.3 to install the pod (if you also need to write a Podfile as well). You can use CocoaPods version 1.5.3 to install the pod. We’re all iOSers and I’m going to skip them here).

$bundle exec pod install Analyzing dependencies and dependencies package SnapKit (5.0.1) CPU 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.

Take a look at the podfile.lock file again:

Dependencies: -snapkit (~> 5.0.0) SPEC REPOS: -snapkit (~> 5.0.0) https://github.com/cocoapods/specs.git: - SnapKit SPEC CHECKSUMS: SnapKit: 97 b92857e3df3a0c71833cce143274bf6ef8e5eb PODFILE CHECKSUM: 1 a4b05aaf43554bc31c90f8dac5c2dc0490203e8 COCOAPODS: 1.5.3

I found that I was using CocoaPods version 1.5.3. When we don’t use the bundle exec execution prefix, we use the CocoaPods version in the system environment. We have thus proved that the Gem environment in the project and the environment in the system can be separated by Bundler.

conclusion

  • From the perspective of the evolution of version management tools, we can see that the birth of CocoaPods is not achieved overnight, but also continuously learn from the advantages of other management tools and develop little by little. VCS tools from the early daysSVN,GitAnd subdivide itGit Submodule, and then to every languagePackage ManagerIt’s developing all the time.
  • althoughCocoaPodsAs a package management tool, it controls the various libraries that depend on the iOS project, but it also follows strict version control and iterates. I hope you can see the importance of versioning from this article.
  • Through the fieldBundlerTo manage the whole process of engineeringBundlerFundamentals and learned how to control in a projectGemVersion information.

Later we will focus on CocoaPods, moving from the tool chain to the details and explaining each one based on our experience with it.

Knowledge point problem carding

Here are four questions to check if you have mastered this article, and if not, to bookmark it again:

  1. PMHow do you versioning dependent libraries?
  2. Ruby 和 RVM/rbenvWhat is the relationship between?
  3. Gem,Bundler 和 CocaPodsWhat is the relationship between?
  4. How to useBundlerTo manage the projectGemThe environment? How do you lock the inside of the projectCocoaPodsVersion?