How SoulAPP Cut iOS Build Times by 30 Minutes: A Deep Dive into Compilation Optimizations
This article details how SoulAPP reduced its iOS clean‑build time from around 30 minutes to just a few minutes by applying precompiled headers, Xcode build‑setting tweaks, component‑based architecture, binary caching with Rugby, and targeted CI optimizations.
1. Single‑Project Phase
1.1 Xcode Build Process
Pre‑process: macro expansion, comment removal, header inclusion, producing .i files.
Compile: converting .i to assembly .s files.
Assemble: turning .s into object .o files.
Link: resolving symbols and creating the final executable.
1.2 Compilation‑Option Tweaks
Set Debug Information Format to DWARF to avoid full symbol tables.
Enable Build Active Architecture Only for Debug builds.
Optimize header search paths to eliminate recursive -I entries.
Set Swift Compilation Mode to Incremental for Debug and Whole Module for Release.
Use No Optimization (‑O0) for Debug.
Disable Index‑While‑Building to defer indexing to idle time.
These adjustments saved roughly two minutes per build, with header‑search‑path optimization contributing the most.
2. Componentized Project Phase
2.1 Goals and Benefits
Decouple modules so changes in one do not affect others.
Enable independent compilation of modules, reducing overall build time.
Clarify module boundaries for better teamwork.
Improve project maintainability.
Facilitate reuse of component functionality.
2.2 Binary Compilation Optimization
The team introduced binary caching for many components, similar to cocoapods‑binary. A custom automation script packages components as pre‑compiled frameworks, cutting total compile time by about 20 minutes.
A custom CocoaPods plugin reads component versions from a JSON file linked to the internal R&D platform, allowing seamless switching between source and binary modes.
3. Full‑Source Compilation Optimization
The team evaluated Google Bazel but found the learning curve and integration effort prohibitive. Instead, they adopted the open‑source Rugby project (https://github.com/swiftyfinch/Rugby), which converts CocoaPods pods into pre‑compiled frameworks and caches them.
Typical usage: rugby build --arch arm64 --sdk ios To exclude a pod from caching:
rugby build --arch arm64 --sdk ios -e RangersAppLogPerformance comparison on an Intel machine with 16 GB RAM:
Full source build without Rugby: 1141 s.
Full source build with Rugby: 980 s.
Switching a component source↔binary without Rugby: 1150 s.
Switching the same component with Rugby: 150 s.
The improvement stems from avoiding a full pod install ‑triggered rebuild; only the changed component is recompiled and linked.
4. Ancillary Tools
An internal regex‑based tool scans the codebase weekly for unused classes, resources, and scripts, helping to prevent resource bloat.
5. Summary
By applying stage‑specific compilation strategies—starting with precompiled headers, then modularizing the codebase, and finally leveraging binary caching with Rugby—the clean‑build time was reduced from roughly 30 minutes to a few minutes, dramatically improving developer productivity and CI throughput. Additional tooling such as Xcode Build Timing Summary, Build Timeline, and XCLogParser (https://github.com/MobileNativeFoundation/XCLogParser) were used to measure and analyze build performance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
