At Zomato, we tend to iterate incredibly fast in an ecosystem composed of different teams, all working together to build great products.
In the case of our iOS app, this is an on-going process of enhancements with the sole aim of ensuring an even smoother user experience.
One of the biggest hurdles we faced during this process, was the per unit time it took to implement an iteration. This directly points to the time it took to make a change, build the project and deploy updates to the device with our bottleneck being the total build time.
How did our metrics look before?
- Incremental builds — 80 seconds
- Clean builds — 170 seconds
- Cold starts — 200 seconds
We definitely had to improve these numbers across the board if we wanted to move fast in our development cycles.
How did we optimise our build time?
The first step for us was to make sure our project settings were optimally configured for Debug environments. This would entail –
- Avoiding building dsyms (debug symbols) – which is a trade-off between having symbols available while debugging and compile time. You can configure this under project settings in Xcode
- Setting Compilation Mode to Incremental and Optimisation Mode to On
- Building for active architectures only when compiling our project
Moving forward we made the following amendments to our project to improve build times :
- Minimising run script phases — Reduced run script phases with clearly defined inputs and outputs
- New Build System — Enabled the new build system in Xcode, which provides a ludicrous speedup of up to 80% on incremental builds
- Pre-built Frameworks — Prevented re-compiling third party source by using pre-built dynamic frameworks/libraries
- Modular Frameworks — Split up bloated internal frameworks and libraries to improve parallelisation
- Succinct Framework Headers— Specified correct access modifiers for all classes, structs and enums across all our internal frameworks
- Remove Unused Source Code— Removed files being compiled which are not invoked from the current codebase
What did we achieve?
The resulting build time after following through looks something like this :
- Incremental builds — 0.8 seconds
- Clean builds — 81 seconds
- Cold starts — 95 seconds
That’s a 99% decrease in incremental build time and more than a 50% reduction for a cold start.
Here’s a breakdown of the build time before and after our changes –
For us, not only did build time decrease significantly due to the action points listed above, but we also managed to reduce our app size by 6MB and our launch times by 400ms, bringing this under a second on most modern iPhones.
Care for some profiling tips?
Few tricks you can use to help with visibility when tackling compile times in Xcode —
- Swift function compile times can be measured by setting -Xfrontend -warn-long-function-bodies=100, where the value 100 indicates a cap of 100ms to compile
- You can tell Xcode to log build times in the status bar using the following command
defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES
- Build with timing summary is an option you can choose Product-> Perform Action -> Build with timing summary, which will let you see the time taken to compile source files and process execution when Xcode builds your project
WWDC has some really great videos about how Xcode builds your project, which are definitely worth checking out –