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) – whichis 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
Build with timing summary is an option you can choose Product-> Perform Action -> Buildwith timing summary, which will let you see the time taken to compile source files and process execution when Xcode builds your project
References
WWDC has some really great videos about how Xcode builds your project, which are definitely worth checking out –
Discover how we migrated our observability metrics platform from Thanos and Prometheus to VictoriaMetrics for cost reduction, enhanced reliability and scalability.
Read more about how we used GOMEMLIMIT in 250+ microservices to tackle OOM issues and high CPU usage in Go applications, significantly enhancing performance and reliability.