CocoaPods Explained: Podfile
In iOS development, CocoaPods has become one of the must-know tools. In a team where CocoaPods is adopted, engineers run pod install
as often as git checkout
. They know how to properly set up a project with CocoaPods, declare dependencies in Podfile, and update dependencies if needed. However, some have little understanding of what happens under the hood. This prevents them from troubleshooting CocoaPods issues if occurred or extending its usage. The “CocoaPods Explained” series I am writing aims to fill that gap.
In this post, we take a look at Podfile and try to understand it a little bit more.
Declaration in Podfile
Podfile is where we declare dependencies of the project. This file is just a snippet of ruby code on top of provided methods (reference: Podfile syntax).
platform :ios, "14.0"
use_frameworks!
target "App" do
pod "RxSwift"
# ...
end
In Podfile, we declare a dependency using the pod
function. Such a dependency could be from CocoaPods-managed repos, a git repo, or a local directory:
pod "RxSwift", "6.1.0"
pod "Moya", :git => "https://github.com/Moya/Moya.git", :tag => "15.0.3"
pod "Services", :path => "LocalPods/Services"
When is Podfile loaded?
Not all pod commands load this Podfile. We can easily verify this hypothesis by printing a log message at the beginning of Podfile (ex. prepending this line puts "Load Podfile"
). The message appears in the logs when running pod install
while it does not when running some other commands like pod repo list
.
This behavior makes sense because Podfile is meant for dependencies declaration. CLI usages related to dependencies should load Podfile. Otherwise, loading Podfile is not necessary. Looking at the pod subcommand descriptions (ie. running pod --help
), one may intuitively tell that pod install
, pod update
, and pod outdated
do load Podfile.
Looking at the code of cocoapods and cocoapods-core, Podfile is loaded when we first access Pod::Config.instance.podfile
(ie. the attribute podfile
of the config singleton).
https://github.com/CocoaPods/CocoaPods/blob/1.11.0/lib/cocoapods/config.rb#L204-L206
module Pod
class Config
def podfile
@podfile ||= Podfile.from_file(podfile_path) if podfile_path
end
...
end
end
We can verify this easily by executing a one-line ruby code that accesses the value. The log Load Podfile
should appear in the console.
$ ruby -e 'require "cocoapods"; Pod::Config.instance.podfile'
Load Podfile
Methods in Podfile
Podfile content is evaluated within Pod::Podfile
context. This gives us code access to Pod::Podfile
’s instance variables and private methods. Therefore, what we can do in Podfile is much more than just dependencies declaration.
Have you ever got annoyed by the Podfile checksum changing all the time? This change causes a high chance of merge conflicts in Podfile.lock, especially when there are many contributors to the project.
-PODFILE CHECKSUM: ad5874f24bb0dbffd1e9bb8443604e3578796c7a
+PODFILE CHECKSUM: ab71b2c6800abc9eba8a3500b110eccf4e33e7f4
We can tackle this problem by setting the @checksum
attribute right in Podfile. After running pod install
, we should see the dummy checksum being reflected in Podfile.lock.
# In Podfile
@checksum = "dummy-text-to-reduce-merge-conflicts"
…
As mentioned, we have access to Pod::Podfile
’s methods in Podfile. Those commonly used in Podfile are use_frameworks!
, platform :ios, "14.0"
, target "..."
, pod "..."
. In fact, those methods are defined in Pod::Podfile::DSL
and included in Pod::Podfile
.
We can patch those methods to add more customizations. This could be done using alias_method
with the corresponding method in Pod::Podfile
or Pod::Podfile::DSL
. Using alias_method
is similar to the method swizzling technique in Swift. Some CocoaPods plugins alter CocoaPods’s behaviors this way to interfere with the pod installation process.
The following code demonstrates how we introduce the :xcode_migration
option to the pod
syntax. This option helps eliminate many if/else checks in Podfile, making the usage more declarative.
# In Podfile
require_relative "patch" # <-- Load patch.rb
...
pod "RxSwift", :xcode_migration => {
:default => "6.1.0",
:xcode14 => {
:git => "link/to/fork",
:branch => "xcode14"
}
}
# In patch.rb
module Pod
class Podfile
alias_method :original_pod, :pod
def pod(*args, **kwargs)
if (opts = kwargs[:xcode_migration])
# Process opts & update args/kwargs to use the version/branch accordingly
...
if xcode14?
...
end
end
original_pod(*args, **kwargs) # <-- call the original method with the processed args/kwargs
end
end
end
…
Above are some explanations of how Podfile works. More topics will be covered in the upcoming posts. Stay tuned!
Like what you’re reading? Buy me a coffee and keep me going!
Subscribe to this substack
to stay updated with the latest content