Why Spotify Built a Remote Cache for Xcode

Spotify has open sourced XCRemoteCache, a remote caching library for iOS projects that reuses Xcode target artifacts produced on CI machines. The tool supports Objective-C, Swift, and mixed ObjC+Swift targets, and works with projects managed by CocoaPods, Carthage, or custom dependency setups. In production, it cut median clean build times by 70% (a clean build being one where at least 50% of targets compile at least one file).

Feature Image

Build metrics from Spotify's open source XCMetrics project showed that building the main Spotify iOS app often took developers more than 10 minutes. Although those long builds made up less than 3% of all builds, they accounted for more than half of total build time. The pattern pointed to rebasing or merging remote branches as the trigger, which made a remote cache the obvious fix.

Caching at the Target Level

Remote caching follows the "compile once, use everywhere" principle: when inputs and compilation parameters match, download prebuilt artifacts instead of rebuilding locally. The key decision is granularity. Too fine-grained — caching individual compilation steps — creates network overhead that can wipe out CPU savings. Too coarse — treating the whole codebase as one unit — means any local edit invalidates everything.

Spotify's iOS app is highly modular, with more than 400 independent modules as separate Xcode targets. That made target-level caching the natural fit.

How XCRemoteCache Identifies Input Files

All caching relies on fingerprinting the input files to know whether build products can be reused. But Xcode complicates that: its build system is generous with dependency attribution and will optimistically resolve headers or .swiftmodule files from any available search path — including ones the developer never explicitly listed. Hashing everything in those paths would produce fingerprints that are far too broad.

However, Xcode handles local incremental builds well, executing only the narrow subset of steps affected since the last build. The build system effectively knows the real input files, but writes that list (.d files) only as compiler output — after compilation, not before.

XCRemoteCache works around this by combining Git history with compiler-generated dependency lists. In producer mode, which runs on CI for each primary-branch commit, the tool uploads the compilation product plus a meta file listing every file the compiler actually used and the full SHA-1 commit it was built against. In consumer mode, XCRemoteCache finds the most recent commit with ready artifacts, then builds a fingerprint from the input files in the meta file. The strict input list therefore comes almost for free.

Figure 2: Finding a commit with artifacts to reuse.

Handling Path Sensitivity

Portability across machines requires normalizing absolute paths embedded in build products. .swiftmodule files and debug symbols are the problem cases. Swiftmodule files from projects checked out at different paths (e.g., /dir1 vs. /dir2) don't match byte-for-byte, which would cause false cache misses when .swiftmodule is part of the fingerprint set. XCRemoteCache ships a separate, path-agnostic "fingerprint override" file next to each .swiftmodule, containing a fingerprint of all compilation inputs — usable as a stable byte-level fingerprint for Swift targets.

For debug symbols, the tool applies debug-prefix-map on both producer and consumer when invoking Swift and Clang, aligning the source root across all symbols. On the consumer side, LLDB runtime rewrites handle any remaining path differences via settings set target.source-map.

Results from Spotify's Rollout

Spotify's CI was already fast enough that it didn't slow feedback loops, so the focus was on local machines. In controlled tests, XCRemoteCache cut the very first build of the app by 85%. To measure real-world impact, Spotify rolled the tool out to half its developers for a week: median clean builds dropped 70% and incremental builds 6%.

XCRemoteCache has been enabled on the main Spotify app for over a year. As a side effect, developers are now roughly twice as likely to rebase their working branches, which leads to fewer merge conflicts at pull-request time.

Integration and Requirements

The project works with any HTTP server that supports PUT, HEAD, and GET, including Amazon S3 and Google Cloud Storage, plus a provided Docker image for local development. Spotify open sourced a CocoaPods plugin and an automated script that modifies existing .xcodeproj files. For best results, the target project should be split into multiple targets to avoid frequent cache invalidations.

Integration steps are documented in the How-to section of the repository. The tool itself is written entirely in Swift, and Spotify invites contributions and issue reports for Xcode setups not yet covered. A development guide is available for contributors.