(p.2) Cozy pod install herb.svg

31.05.2020
5 min

In last part, we cut down the bundle exec pod install command to pods install. Just one letter more than the usual pod install. And this time let's see what other upset moments happen while using this command.

Let's describe which scenarios of using pod install exist. But first, need to figure out which files are involved in dependency resolving when running this command:

  • Podfile describes the necessary dependencies;
  • Podfile.lock describes the installed dependencies;
  • Manifest.lock a local copy of the Podfile.lock. Located in the Pods folder.

rocket.gif Scenario 1: New project

A new project without CocoaPods. Call pod init, then describe the dependencies in Podfile. Finally, call pod install for the first time. At the time of the call CocoaPods is looking that there is no Podfile.lock file yet. Then installs all the dependencies described in Podfile. In the end, Podfile.lock and its local copy Manifest.lock are created.

edit.svg Scenario 2: Changed something

In this scenario, we change Podfile or *. podspec for the local dependency. Maybe we added a new dependency or changed the description. When called, Podfile.lock and its local copy Manifest.lock are updated.

In my opinion, there is nothing to optimize in the first two scenarios. But we would not break them with our future optimization.

sync.svg Scenario 3: Sync

Download a new project or new changes from git where Podfile.lock already exists. When the command is called, the dependencies from two files are usually compared: Podfile.lock and your local file Manifest.lock. Dependencies from Podfile.lock are updated and copied to Manifest.lock.

Everything is as usual, but there is one caveat. The project will be regenerated even if Podfile.lock and Manifest.lock are the same. In other words, if pod install was not needed, and you called it, then it will not work instantly. And will not say that it was not needed at all now.

On my working project, this unnecessary run takes a full minute. Maybe this is not so much, but obviously, this time can be used more usefully.

xcode.svg Solving the problem in Xcode

A solution to this problem is already in Build Phases of the project generated using CocoaPods. That is. You just always need to first try to build the project, and then call pod install in the terminal.

In this script, Podfile.lock and Manifest.lock are compared using the diff command. And if they are not equal, then Xcode will have a familiar error The sandbox is not in sync with the Podfile.lock…

In general, this is a great solution, but what if you still forgot? Opened the terminal and called first the unnecessary pod install? Or your project is huge and working slowly?

Just wait a minute! - you would say. But I can’t wait. Moreover, this is not the last thing I would like to make more convenient in pod install. And it's so magnetic work with Bash.

bender.svg Our own pod install with blackjack

Why not? Well, no way. It is for a very long time and senseless. And also need to support the release of new versions that can break everything.

But why not just write a small wrapper over CocoaPods and add a check before calling pod install, as is done in Xcode? Can we?

All that we will do needs to be placed in your .bash_profile, as we did in the last part. First, create a simple wrapper function pods_install. With this function, we can add a check before calling pod install. And much more.

function pods_install() {
# TODO: Add compare Podfile.lock and Manifest.lock
bundle exec pod install "$@"
}

Then we need to make sure that everything works exactly as before. And will not break something. Instead of alias from the previous post, we will create the pods function. If the argument install is passed to this function, we will throw away this argument and call pods_install. Otherwise, we just replace pods with bundle exec pod.

# Instead of the alias from the last post
function pods() {
case $* in
install ) shift 1; pods_install "$@" ;;
* ) command bundle exec pod "$@" ;;
esac
}

So far so good. Everything still works as before. Remains to add the check to pods_install. I copied the code from Xcode and cut down it as much as possible for simplicity. The diff command is used for comparison. The command log is not output to the terminal. After execution, we check the value of the result of the command. If it is zero the files are the same. Otherwise, we exit the function and skip pod install.

function pods_install() {
diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null
if [ $? == 0 ] ; then
return
fi
bundle exec pod install "$@"
}

bug.svg Debug

Let's check all the scenarios:

  • New project. When called, we get diff: Podfile.lock: No such file or directory. Then pod install is executed and Podfile.lock is created. That's OK;
  • Sync. If Podfile.lock was not changed in git, then pod install is not called. Else the local Manifest.lock is different from Podfile.lock, so pod install is called. Jobs done;
  • We changed something in Podfile or *.podspec for the local dependency. Nothing happens when calling pod install. Because Podfile.lock and Manifest.lock remain the same. This is bad, we have to figure out how can force pod install to run. That's buggy.

force.svg Force pod install

The easiest way to fix the buggy scenario is to add a new mode. In this mode, we will always call pod install without checking Podfile.lock and Manifest.lock. We always know when we added a new dependency or changed local *.podspec.

Let's change the pods_install function first. For simplicity, we will check only the first argument. If it is -f, then skip the step where we compare Podfile.lock and Manifest.lock. Otherwise, skip the first argument and pass everything else to pod install.

function pods_install() {
# Check the first argument and if it is -f
if [ "$1" == "-f" ] ; then
# Throw it away so as not to pass it to pod install
shift 1;
else
# Otherwise all as before
diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null
if [ $? == 0 ] ; then
return
fi
fi
bundle exec pod install "$@"
}

It remains to slightly update the pods function. Again, for simplicity, I decided to use the pods install! command. It is slightly shorter to use and easier to implement. In the function, we check if the argument is install!, when pods_install is called, we pass the first argument -f, and then the remaining arguments. The rest is unchanged.

function pods() {
case $* in
install! ) shift 1; pods_install -f "$@" ;;
install ) shift 1; pods_install "$@" ;;
* ) command bundle exec pod "$@" ;;
esac
}

loudspeaker.svg Add message

Add a small message that this time there were no changes and pod install was not called. The final version will look like this.

function pods_install() {
if [ "$1" == "-f" ] ; then
shift 1;
else
diff "Podfile.lock" "Pods/Manifest.lock" > /dev/null
if [ $? == 0 ] ; then
echo "party.png Everything is up to date."
return
fi
fi
bundle exec pod install "$@"
}
function pods() {
case $* in
install! ) shift 1; pods_install -f "$@" ;;
install ) shift 1; pods_install "$@" ;;
* ) command bundle exec pod "$@" ;;
esac
}

party.png Result

  • In this part, we made pod install a little more convenient;
  • Prepared Bash function for new enhancements;
  • And didn’t break anything (but I'm not sure).
pods install
party.png Everything is up to date.
pods install!
Analyzing dependencies
Downloading dependencies
Installing Alamofire (5.2.0)
Pod installation complete!
There are 2 dependencies from the Podfile and 2 total pods installed.

In the next part we will try to make pod install even more convenient. We will learn how to calmly switch to drink coffee and watch memes other important tasks, and not to constantly check the terminal in the hope that everything is already installed.


💬 Please, leave some feedback in Twitter post.
It will help me improve posts and continue to publish new ones.