Module cache
Module cache {#module-cache}
Section titled “Module cache {#module-cache}”::: warning REQUIREMENTS
- A
generated project - A
Tuist account and project
:::
Tuist Module Cache provides a powerful way to optimize build times by caching your modules as binaries (.xcframeworks) and sharing them across different environments. This capability allows you to leverage previously generated binaries, reducing the need for repeated compilation and speeding up the development process.
Warming {#warming}
Section titled “Warming {#warming}”Tuist efficiently
This operation, known as “warming,” produces binaries for local use or for sharing with teammates and CI environments via Tuist. The process of warming the cache is straightforward and can be initiated with a simple command:
tuist cacheThe command re-uses binaries to speed up the process.
Usage {#usage}
Section titled “Usage {#usage}”By default, when Tuist commands necessitate project generation, they automatically substitute dependencies with their binary equivalents from the cache, if available. Additionally, if you specify a list of targets to focus on, Tuist will also replace any dependent targets with their cached binaries, provided they are available. For those who prefer a different approach, there is an option to opt out of this behavior entirely by using a specific flag:
::: code-group
tuist generate # Only dependenciestuist generate Search # Dependencies + Search dependenciestuist generate Search Settings # Dependencies, and Search and Settings dependenciestuist generate --cache-profile none # No cache at alltuist test:::
::: warning
Binary caching is a feature designed for development workflows such as running the app on a simulator or device, or running tests. It is not intended for release builds. When archiving the app, generate a project with the sources by using --cache-profile none.
:::
Cache profiles {#cache-profiles}
Section titled “Cache profiles {#cache-profiles}”Tuist supports cache profiles to control how aggressively targets are replaced with cached binaries when generating projects.
- Built-ins:
only-external: replace external dependencies only (system default)all-possible: replace as many targets as possible (including internal targets)none: never replace with cached binaries
Select a profile with --cache-profile on tuist generate:
# Built-in profilestuist generate --cache-profile all-possible
# Custom profiles (defined in Tuist Config)tuist generate --cache-profile development
# Use config default (no flag)tuist generate
# Focus on specific targets (implies all-possible)tuist generate MyModule AnotherTarget
# Disable binary replacement entirelytuist generate --cache-profile none::: info DEPRECATED FLAG
The --no-binary-cache flag is deprecated. Use --cache-profile none instead. The deprecated flag still works for backwards compatibility.
:::
Precedence when resolving the effective behavior (highest to lowest):
--cache-profile none- Target focus (passing targets to
generate) → profileall-possible --cache-profile <value>- Config default (if set)
- System default (
only-external)
Supported products {#supported-products}
Section titled “Supported products {#supported-products}”Only the following target products are cacheable by Tuist:
- Frameworks (static and dynamic) that don’t depend on XCTest
- Bundles
- Swift Macros
We are working on supporting libraries and targets that depend on XCTest.
::: info UPSTREAM DEPENDENCIES
When a target is non-cacheable it makes the upstream targets non-cacheable too. For example, if you have the dependency graph A > B, where A depends on B, if B is non-cacheable, A will also be non-cacheable.
:::
Efficiency {#efficiency}
Section titled “Efficiency {#efficiency}”The level of efficiency that can be achieved with binary caching depends strongly on the graph structure. To achieve the best results, we recommend the following:
- Avoid very nested dependency graphs. The shallower the graph, the better.
- Define dependencies with protocol/interface targets instead of implementation ones, and dependency-inject implementations from the top-most targets.
- Split frequently-modified targets into smaller ones whose likelihood of change is lower.
The above suggestions are part of the
Recommended setup {#recommended-setup}
Section titled “Recommended setup {#recommended-setup}”We recommend having a CI job that runs in every commit in the main branch to warm the cache. This will ensure the cache always contains binaries for the changes in main so local and CI branch build incrementally upon them.
::: tip KEEP CACHE WARMING ISOLATED
Run tuist cache in a dedicated CI step without subsequent steps that depend on the generated workspace. Since tuist cache modifies the workspace for cache building purposes, any CI steps that need the workspace should run tuist generate first to get a fresh, usable workspace.
::: tip CACHE WARMING USES BINARIES
The tuist cache command also makes use of the binary cache to speed up the warming.
:::
The following are some examples of common workflows:
A developer starts to work on a new feature {#a-developer-starts-to-work-on-a-new-feature}
Section titled “A developer starts to work on a new feature {#a-developer-starts-to-work-on-a-new-feature}”- They create a new branch from
main. - They run
tuist generate. - Tuist pulls the most recent binaries from
mainand generates the project with them.
A developer pushes changes upstream {#a-developer-pushes-changes-upstream}
Section titled “A developer pushes changes upstream {#a-developer-pushes-changes-upstream}”- The CI pipeline will run
xcodebuild buildortuist testto build or test the project. - The workflow will pull the most recent binaries from
mainand generate the project with them. - It will then build or test the project incrementally.
Configuration {#configuration}
Section titled “Configuration {#configuration}”Cache concurrency limit {#cache-concurrency-limit}
Section titled “Cache concurrency limit {#cache-concurrency-limit}”By default, Tuist downloads and uploads cache artifacts without any concurrency limit, maximizing throughput. You can control this behavior using the TUIST_CACHE_CONCURRENCY_LIMIT environment variable:
# Set a specific concurrency limitexport TUIST_CACHE_CONCURRENCY_LIMIT=10tuist generate
# Use "none" for no limit (default behavior)export TUIST_CACHE_CONCURRENCY_LIMIT=nonetuist generateThis can be useful in environments with limited network bandwidth or to reduce system load during cache operations.
Troubleshooting {#troubleshooting}
Section titled “Troubleshooting {#troubleshooting}”It doesn’t use binaries for my targets {#it-doesnt-use-binaries-for-my-targets}
Section titled “It doesn’t use binaries for my targets {#it-doesnt-use-binaries-for-my-targets}”Ensure that the diff command to compare the projects generated by two consecutive invocations of tuist generate or across environments or runs.
Also make sure that the target doesn’t depend either directly or indirectly on a
Missing symbols {#missing-symbols}
Section titled “Missing symbols {#missing-symbols}”When using sources, Xcode’s build system, through Derived Data, can resolve dependencies that are not declared explicitly. However, when you rely on the binary cache, dependencies must be declared explicitly; otherwise you’ll likely see compilation errors when symbols can’t be found. To debug this, we recommend using the tuist inspect dependencies --only implicit