Summary
Introduce a way to specify Xcode’s “Apply Each Build Rule” folder references in Tuist projects to improve scalability and build performance when dealing with very large collections of resources, such as .xib, .strings, .xcassets, and other file types that rely on Xcode’s internal build rules.
Need/problem
tuist becomes really slow when project becomes bigger with a lot of resources to process. I maintain a project that contains 10000+ resources files to process a build rule ( asset, xib, strings, xcstring …)
using glob pattern is slow and unreliable. (takes 60+ sec)
However, applying Folder pattern is super fast (takes only 10 sec).
But in this case xcode just copy the whole folder without processing the default build rule (xib does not compiles into nib)
This is because, tuist applies “Apply Once build rule”,
But xcode has another option “Apply Each Build rule”,
which automatically scan the sub folder and applies appropriate build rules ( compiles source file, compile xib, process catalogs … etc automatically!)
I expect tuist to add possibility to specify the “Build Rules” policy for “Folder” reference.
Motivation
Tuist becomes noticeably slow when managing large projects with many resources. For example, in our project, we manage over 10,000+ resource files, including .xib, .strings, .xcassets, .xcstrings, etc.
Currently, Tuist relies on glob patterns to match these files, but:
- Glob is slow: Tuist performs file scanning at manifest generation time, which leads to build times of 60+ seconds.
Xcode, on the other hand, supports a mode where a folder is referenced directly and each file within it is evaluated and sync automatically using “Apply Each Build Rule” . This makes a huge difference:
- Folder reference build time: ~10s
- Glob pattern resource build time: ~60s
- But… Tuist uses “Apply Once Build Rule” for folders by default, which just copies the folder as-is, skipping resource processing (e.g., .xib files are not compiled into .nib).
Moreover recent version of xcode project generation is based on “Apply Each Build Rule” folder too. Which is more like swift package like behavior.
We want Tuist to support “Apply Each Build Rule” for folder references so that:
- Xcode itself is responsible for applying the appropriate build rules
- Tuist project generation time will significantly improved (no glob pattern, and searching files)
Detailed design
We propose adding a new property to Target in ProjectDescription:
“Apply Each Build Rule” folder is not a source folder nor resource folder.
It is deferred until xcode process filesystem sync.
So I think it should be introduced as new property.
public struct Target {
// ...
/// These folders will be passed to Xcode as "folder references" with the
/// "Apply Each Build Rule" policy.
///
/// Xcode will automatically:
/// - Compile `.xib` → `.nib`
/// - Process `.xcassets`
/// - Compile source files if present
/// - Apply any custom build rules
public var folderReferences:[ProjectDescription.Path]
}
Tuist will map these folders into .pbxproj as folder references using the PBXFileReference settings that enable “Apply Each Build Rule” behavior.
Important Notes:
- These are not resources, sources, or headers. They’re independent and defer build rule application to Xcode.
- Order of phases is preserved — Xcode will insert these files in the correct build phases.
- This field is additive and does not break compatibility.
- Tuist “SynthesizedResourceAccessors” can not generate accessor for resources reference by this
Drawbacks
-
Tuist “SynthesizedResourceAccessors” can not generate accessor for resources reference by this
-
Not sure, which xcode version starts supporting “Apply Each Build Rule” Folder reference
-
“Apply Each Build Rule” folder creates extra section in PBX project file named “fileSystemSynchronizedGroups”, and treat that item as children
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
17C3D7862E306FFA002E68D0 /* active */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = active; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
//...
194ABDC17482D4F79CAEA794 /* AppLocalization */ = {
isa = PBXGroup;
children = (
17C3D7862E306FFA002E68D0 /* active */,
);
path = MafiaLocalization;
sourceTree = "<group>";
};
Alternatives
• | Use resources: [Resources/**] (current Tuist behavior) | |
---|---|---|
• | ![]() |
|
• | ![]() |
|
• | ![]() |
Adoption strategy
- This feature is non-breaking.
- Existing Target definitions remain valid.
How We Teach This
- This concept can be introduced as:
“Folder-based inputs that Xcode processes directly using its own build rules.”
- In documentation:
- Explain the difference between resources, sources, and folderReferences
- Show performance comparison on generating pbx project file.
- Provide migration guides: “Convert resources: [“Resources/**”] to folderReferences: [“Resources”]”
- Explain that tuist can not generate ResourceAccessor for FolderReference.
- In code comments:
folderReferences is best used for large folders with mixed resources (xib, xcassets, strings) to leverage Xcode’s optimized build rules.