{"id":3586,"date":"2026-03-16T05:06:03","date_gmt":"2026-03-16T05:06:03","guid":{"rendered":"https:\/\/livsycode.com\/?p=3586"},"modified":"2026-03-16T05:06:04","modified_gmt":"2026-03-16T05:06:04","slug":"xcode-26-compilation-cache","status":"publish","type":"post","link":"https:\/\/livsycode.com\/best-practices\/xcode-26-compilation-cache\/","title":{"rendered":"Xcode 26 Compilation Cache"},"content":{"rendered":"\n<p>Greetings, traveler!<\/p>\n\n\n\n<p>Most iOS engineers don\u2019t need another reminder that builds are expensive \u2014 we feel it every day.<\/p>\n\n\n\n<p>You change a few lines. You wait. You switch branches. You wait again. CI rebuilds the same targets for the tenth time today. Someone suggests cleaning DerivedData. The build gets slower, then faster, then weird again.<\/p>\n\n\n\n<p>For years, we treated this as a fact of life: large Swift projects compile slowly, and the best you can do is keep the project modular and hope the compiler behaves.<\/p>\n\n\n\n<p>Xcode 26 introduces a feature that changes the situation in a more fundamental way: <strong>Compilation Cache<\/strong>. The goal isn\u2019t to make the compiler 5% faster. The goal is to stop repeating work that has already been done.<\/p>\n\n\n\n<p>Let\u2019s break down what this cache actually means, when it pays off, and where it doesn\u2019t.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The repeating-work problem<\/h2>\n\n\n\n<p>Consider a typical week in a team:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>you\u2019re working on a feature branch<\/li>\n\n\n\n<li>two colleagues are doing the same in parallel<\/li>\n\n\n\n<li>a CI runner builds every PR from scratch<\/li>\n\n\n\n<li>the same dependencies and internal modules are compiled over and over again<\/li>\n<\/ul>\n\n\n\n<p>A large portion of this work is redundant. It\u2019s rebuilding because the build system has no reliable way to reuse the previous result.<\/p>\n\n\n\n<p>This is the exact problem compilation caching is designed to solve.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why DerivedData never solved it<\/h2>\n\n\n\n<p>Xcode has always stored artifacts \u2014 object files, intermediates, indexing outputs. But DerivedData was never built to support reusable caching across builds in a robust way.<\/p>\n\n\n\n<p>The easiest way to see it: DerivedData is treated as disposable.<\/p>\n\n\n\n<p>If you\u2019ve been on iOS long enough, you\u2019ve probably heard all of these:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u201cclean build folder fixes it\u201d<\/li>\n\n\n\n<li>\u201ctry removing DerivedData\u201d<\/li>\n\n\n\n<li>\u201cmaybe Xcode got confused\u201d<\/li>\n<\/ul>\n\n\n\n<p>That works because DerivedData is a local build workspace. It\u2019s convenient and practical, but it\u2019s not a structured cache with correctness guarantees.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Compilation Cache changes<\/h2>\n\n\n\n<p>With Xcode 26, compilation results can be cached in a more intentional and reusable way.<\/p>\n\n\n\n<p>The key shift is this: Xcode can now decide whether a compilation action is reusable based on what went into it.<\/p>\n\n\n\n<p>If the relevant inputs didn\u2019t change (sources, compiler settings, toolchain, etc.), Xcode can skip repeating the work and pull the result from cache.<\/p>\n\n\n\n<p>This often improves two common workflows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>rebuilding after switching branches<\/li>\n\n\n\n<li>repeated clean builds once the cache is warmed up<\/li>\n<\/ul>\n\n\n\n<p>In other words: the cache targets \u201cI already compiled this exact thing yesterday (or five minutes ago)\u201d situations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Enabling it<\/h2>\n\n\n\n<p>Compilation Cache can be enabled via build settings (including <code>xcodebuild<\/code>), which makes it approachable for both local development and CI.<\/p>\n\n\n\n<p>In many projects you can enable it by setting:<\/p>\n\n\n\n<p><code>COMPILATION_CACHE_ENABLE_CACHING = YES<\/code><\/p>\n\n\n\n<p>Once enabled, it builds up over time as you compile.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where you\u2019ll notice the biggest difference<\/h2>\n\n\n\n<p><strong>1) Branch switching<\/strong><\/p>\n\n\n\n<p>Some branches touch parts of the project that trigger a rebuild of modules you didn\u2019t edit directly.<\/p>\n\n\n\n<p>When cache hits work well, you avoid recompiling a large portion of that unchanged code.<\/p>\n\n\n\n<p><strong>2) Clean builds that aren\u2019t truly \u201ccold\u201d<\/strong><\/p>\n\n\n\n<p>A clean build used to mean: \u201cyou\u2019re paying the full cost.\u201d<\/p>\n\n\n\n<p>With compilation caching enabled, even rebuilds after cleaning build products can reuse previously compiled artifacts \u2014 as long as the compilation cache is still present on disk.<\/p>\n\n\n\n<p><strong>3) High-churn CI<\/strong><\/p>\n\n\n\n<p>Many teams rebuild the same dependency graph dozens of times daily.<\/p>\n\n\n\n<p>If your CI setup persists caches between runs, you reduce a large chunk of repeated work.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why some projects won\u2019t see dramatic wins (yet)<\/h2>\n\n\n\n<p>This is where expectations matter.<\/p>\n\n\n\n<p>Even if compilation becomes fast, builds can still be slow because compilation is only one piece of the pipeline.<\/p>\n\n\n\n<p>Common non-compiler bottlenecks:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>asset catalog processing<\/li>\n\n\n\n<li>large copy phases (thousands of files)<\/li>\n\n\n\n<li>heavy script phases (SwiftLint, codegen)<\/li>\n\n\n\n<li>linking and embedding<\/li>\n<\/ul>\n\n\n\n<p>If your build time is dominated by these steps, compilation caching won\u2019t feel like magic.<\/p>\n\n\n\n<p>It\u2019s doing its job \u2014 you just have other bottlenecks.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A note on Swift packages and modular graphs<\/h2>\n\n\n\n<p>Many modern iOS projects are modular, and a lot of that modularity is implemented with Swift Packages.<\/p>\n\n\n\n<p>In theory, modularity helps caching. In practice, cacheability depends heavily on how the build system models the dependency graph and compilation actions.<\/p>\n\n\n\n<p>That means you can observe a slightly counterintuitive outcome:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>a project built primarily from Xcode targets sees clear cache wins<\/li>\n\n\n\n<li>a project with a large Swift Package graph sees smaller improvement<\/li>\n<\/ul>\n\n\n\n<p>This isn\u2019t a reason to avoid SwiftPM. It\u2019s simply the current state of a feature that is still evolving.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to tell if the cache is working for you<\/h2>\n\n\n\n<p>Measure it.<\/p>\n\n\n\n<p>A simple evaluation approach:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Build once to populate cache<\/li>\n\n\n\n<li>Build again with no meaningful code changes<\/li>\n\n\n\n<li>Compare timings<\/li>\n\n\n\n<li>Inspect which phases dominate now<\/li>\n<\/ol>\n\n\n\n<p>Run a second build without changes and open the Build Report. If the total time doesn\u2019t drop, look at the longest steps in the timeline \u2014 scripts, asset processing, and resource copying often dominate once compilation is cached.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Compilation Cache is one of the most practical performance improvements in recent Xcode history.<\/p>\n\n\n\n<p>It won\u2019t fix every slow build, and it won\u2019t replace good build hygiene. But it attacks a specific and very expensive category of waste: <strong>repeating compilation work unnecessarily<\/strong>.<\/p>\n\n\n\n<p>Enable it. Measure it. And once compilation stops being the bottleneck, use that clarity to address the real build pipeline issues your project has been carrying for years.<\/p>\n\n\n\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n<ul class=\"wp-block-social-links has-normal-icon-size has-icon-color has-icon-background-color is-style-default is-horizontal is-content-justification-left is-layout-flex wp-container-core-social-links-is-layout-f551e5d0 wp-block-social-links-is-layout-flex\" style=\"margin-right:0;margin-left:0\"><li style=\"color:#ffffff;background-color:#7a81ff;\" class=\"wp-social-link wp-social-link-linkedin has-white-color wp-block-social-link\"><a rel=\"noopener nofollow\" target=\"_blank\" href=\"https:\/\/www.linkedin.com\/in\/artem-mirzabekian\/\" class=\"wp-block-social-link-anchor\"><svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" version=\"1.1\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z\"><\/path><\/svg><span class=\"wp-block-social-link-label screen-reader-text\">LinkedIn<\/span><\/a><\/li>\n\n<li style=\"color:#ffffff;background-color:#7a81ff;\" class=\"wp-social-link wp-social-link-github has-white-color wp-block-social-link\"><a rel=\"noopener nofollow\" target=\"_blank\" href=\"https:\/\/github.com\/Livsy90\" class=\"wp-block-social-link-anchor\"><svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" version=\"1.1\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z\"><\/path><\/svg><span class=\"wp-block-social-link-label screen-reader-text\">GitHub<\/span><\/a><\/li><\/ul>\n\n\n<div class=\"rpbt_shortcode\">\n<h3>It might be interesting:<\/h3>\n<ul>\n\t\t\t\t\t\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/livsycode.com\/swiftui\/an-odometer-style-number-animation-in-swiftui\/\">An Odometer-Style Number Animation in SwiftUI<\/a>\n\t\t\t<\/li>\n\t\t\t\t\t\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/livsycode.com\/swiftui\/a-reusable-spotlight-onboarding-component-in-swiftui\/\">A Reusable Spotlight Onboarding Component in SwiftUI<\/a>\n\t\t\t<\/li>\n\t\t\t\t\t\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/livsycode.com\/swiftui\/swiftui-withanimation-completion-on-ios-13-16\/\">SwiftUI withAnimation \u0441ompletion on iOS 13\u201316<\/a>\n\t\t\t<\/li>\n\t\t\t\t\t\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/livsycode.com\/swiftui\/swiftui-custom-popover\/\">SwiftUI Custom Popover<\/a>\n\t\t\t<\/li>\n\t\t\t\t\t\n\t\t\t<li>\n\t\t\t\t<a href=\"https:\/\/livsycode.com\/swiftui\/infinite-scrollview-in-swiftui\/\">Infinite ScrollView in SwiftUI<\/a>\n\t\t\t<\/li>\n\t\t\t<\/ul>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Greetings, traveler! Most iOS engineers don\u2019t need another reminder that builds are expensive \u2014 we feel it every day. You change a few lines. You wait. You switch branches. You wait again. CI rebuilds the same targets for the tenth time today. Someone suggests cleaning DerivedData. The build gets slower, then faster, then weird again. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[111],"tags":[109],"class_list":["post-3586","post","type-post","status-publish","format-standard","hentry","category-best-practices","tag-code"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/posts\/3586","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/comments?post=3586"}],"version-history":[{"count":2,"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/posts\/3586\/revisions"}],"predecessor-version":[{"id":3589,"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/posts\/3586\/revisions\/3589"}],"wp:attachment":[{"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/media?parent=3586"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/categories?post=3586"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/livsycode.com\/wp-json\/wp\/v2\/tags?post=3586"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}