Stop using ‘branch’ in your Podfiles!

If you’ve used CocoaPods, at some point, you’ve likely made a pod point to a branch, like this:

pod ‘MYPod’, :git => ‘https://mygit.com/mygroup/myrepo.git’, :branch => ‘my-branch’  

So far, so good. So, why the worry?

The problem lies in some internal processes of Cocoapods.

What’s going on behind the scenes?

When CocoaPods tries to download a dependency pointing to a git repo, it executes the following code:

Basically, it clones the repo. If a commit hash is specified, it will try to checkout that commit.

But how is this clone performed? Let’s see.

Git clone (what?)

CocoaPods creates the clone’s arguments based on certain criteria. The specifics are somewhat unclear, but by analyzing the source code, you can get a good idea of what’s happening behind the scenes.

Here’s a summary of what the function does:

  • Creates the default clone arguments with the URL, the clone location, and an empty template.
  • If it’s a shallow clone (which it is by default, unless CocoaPods identifies that the repo doesn’t support it), and the commit option is not used, it only downloads the branch with a depth of 1. This gets the “surface” code without pulling the history and other data.
  • If CocoaPods detects it shouldn’t force HEAD (which by default it shouldn’t), it will check if a tag or branch was specified and pass that branch to the command.
  • The function returns the arguments, and git clone is executed.

So far, so good, right? Well, maybe not…

Notice the following: when you specify a commit, it will do a full clone of your repo and then checkout the commit’s hash. It’s not the most performant, but it makes sense.

Some repos are huge, storing a lot of history over time. It’s no surprise that even old, simple repos can be over 1GB in size. That’s a big problem: have you ever thought about having to download 1GB or more when doing a pod install, especially with poor internet or data limits?

Ah, but we’re okay. We don’t use commit; we pass a branch, so everything will be fine.

Well, that’s where you’re wrong, and I guarantee you’ll be surprised!!

I swear, it wasn’t me! It was CocoaPods!!

Before pointing fingers, let’s look at the code. What does CocoaPods do with the options you give it?

Here’s the surprise:

Do you know what this ls-remote does? Do you wanna know? Look it!!

> git ls-remote https://meugit.com/meugrupo/meurepo.git minha-branch
cf1a476f6b4f6e75b4e2d4cefcd2389aec56f9bf              refs/heads/minha-branch

It takes your branch and… RETURNS A COMMIT HASH!

Exactly! Cocoapods will turn your branch into a commit…

… AND WILL REMOVE YOUR BRANCH FROM THE OPTIONS!

That’s so cool!(not) 🤡

Now remember what it does when you pass a commit??? That’s right!!!

IT CLONES THE ENTIRE REPO!!! 🤬🤬🤬🤬🤬

Save me! Please!! 😭

Despite the problems, the solution is quite simple. USE A TAG!

When you need to refer to a git repo, use a tag instead of a branch. CocoaPods will not convert your tag to a commit and will perform a shallow clone, making the download infinitely faster.

pod ‘MyPod’, :git => ‘https://mygit.com/mygroup/myrepo.git’, :tag => ‘my-branch’

You don’t necessarily need to create a tag, just change the word “branch” to “tag” in the Podfile. It won’t check if your tag is really a tag, and it will treat it as a branch anyway.

If CocoaPods can deceive you, you can deceive it too! 😈

There is a reason for all of this

CocoaPods doesn’t do this out of malice or a misconception. It’s important when working with pods in development. When you do a pod install pointing to a branch, it converts your branch into a commit and writes that commit to Podfile.lock.

That way, if new things are added to the branch (since it’s in development), it won’t impact the installed pod unless you call `pod update` (which is the intended behavior). This is not the case with a tag, as a tag is not for code under development.

So, don’t curse the tool. Take advantage of the fact that there’s an alternative out there. If you don’t need those commit locks, use tags without restrictions. 😉