Optimise Xcode build to speed Fastlane
The best part of continuous integration is the ability to automatically run tests and build apps, ready to be deployed. However, automatic build doesn’t mean smart or optimised build. Here are some tips I collected along the way to speed up delivery process.
Measuring before improvements, my builds take about 6 minutes after a fresh clean. On the continuous integration side, I use Fastlane on my server which take about 30 minutes. The lanes include clean, run the tests, then build and finally deploy to Fabric.
Let’s see how we can improve those times tackling one issue at a time.
Code optimisation
The more complex a code expression is, the longer the compiler is going to take to the compiler to evaluate it. Here is an example of what to avoid:
return CGSize(width: size.width + (rightView?.bounds.width ?? 0) + (leftView?.bounds.width ?? 0) + 22, height: bounds.height)
To find out which part of your app takes to much time to compile, a developer put together a build time analyzer.
Thanks to this tool, you can quickly identify and improve code for a quick win on your building time. You can also create specific warning in Xcode itself to flag code that takes too much time to compile.
Project optimisation
First, I started by removing the unnecessary code that I couldn’t noticed. What I mean is after some time, if you use Cocoapods to manage libraries, you might also have code you don’t need or use anymore.
To do so I used pod deintegrate && pod install
. The first part would completely remove Cocoapods dependencies from your project, then the second will reinstall it.
By doing that, I managed to remove hundred of files unused, which means thousands of lines of code removed of the compile time.
Another way to speed up the build time, it’s to use swift module optimisation. I would definitely recommend to have a look in Apple documentation Swift.org - Whole-Module Optimization in Swift 3.
In practice, under Build Settings of your project, in Swift Compiler section, you can customise Optimization level. However, picking “Whole Module Optimisation” is not available if you want to keep the ability to debug the app.
A work around is to still be able to debug your app is to keep the flag as -Onone
for your but you have to add SWIFT_WHOLE_MODULE_OPTIMIZATION
to YES
as a custom user settings.
From those changes and after cleaning the project, my build time moved from 5’50” to 3’15”, almost half of the time.
Let’s now have a look to the continuous integration side.
Fastlane optimisation
I use Jenkins to automatically build and prepare new iOS updates using Git flow and Fastlane. Any new feature branch would trigger a new build by running lanes.
Thing I noticed with Fastlane is that scan
builds the project for testing, but gym
also builds for archive and deploy. So we do twice the effort here.
Knowing that both are relying on xcodebuild
command, I tried to use extra arguments in my gym command to be able to run tests against the result. To do so, I used xcargs: " build-for-testing"
.
On the other side, scan has a parameter to test without build. So on the paper, we could use the same derived data folder from my gym test to run my test. To enable this feature, add test_without_building: true
in your scan command.
At the end, the lane process should still be the same:
- clean 🗑
- build (once only) 🛠
- run test ♻️
- distribute 🚀
All together, this 2 days experiencing and investigating how to improve build time help me save some every day. I hope it will help you save some too.
Here are some extra resources to go further