Why is this needed?
Currently, Tuist’s notion of a static framework separates a framework from its resources by putting them in a dedicated Bundle target.
This causes an issue in situations where coupling of code and resources through build settings is required, such as when using automatic string extraction into an xcstrings file (this specific issue is even more important when Xcode 26 ships with powerful AI string manipulations).
This is also important for frameworks with Metal shaders. A recent update to the CLI made it so .metal files are treated as sources (as they should be), which Xcode then compiles according to custom build settings (e.g "MTL_ENABLE_DEBUG_INFO": "INCLUDE_SOURCE" for the Shader debugger), and produces a default.metallib as a framework resource.
However, since Tuist doesn’t see any resources for the framework, it doesn’t generate a Bundle accessor at all.
So I’m relegated to writing code like this to find the metallib in the various contexts where it would be accessed:
let frameworkURL: URL
if ProcessInfo.processInfo.processName == "xctest" {
frameworkURL = Bundle(for: RenderingState.self).bundleURL.appendingPathComponent(
"../SharedMedicalImaging.framework"
)
} else {
frameworkURL = Bundle.main.privateFrameworksURL!.appendingPathComponent("SharedMedicalImaging.framework")
}
let bundle = Bundle(url: frameworkURL)!
guard let library = try? device.makeDefaultLibrary(bundle: bundle)
else { fatalError("Failed to make default library. Make sure provided bundle contains default.metallib") }
In general, to achieve normal static frameworks, I try to create my targets as normal frameworks and specify "MACH_O_TYPE": "staticlib" for each of them.
This works, except that:
- Tuist either doesn’t always generate a Bundle accessor (such as in the Metal shaders case).
- or if it does, since the framework is assumed to be dynamic, it generates a simple accessor
Bundle(for: BundleFinder.self) that always returns the main bundle since our code is statically linked to the main executable, be it a test or an application.
Now I’m not even using Buildable folders, where it looks like the Bundle accessor generation logic takes some other things into account.
Xcode 26 added the #bundle macro, which I hoped would just magically find the right bundle, but alas it hasn’t been able to. If a framework has been compiled statically, it seems to just point to the main bundle.
Steps to address the need
Unfortunately the title is a little misleading because we can’t use the Bundle accessor logic for static frameworks as is, since it looks like it just attempts to find the generated Bundle target.
But a template similar to the code I wrote above for my Metal use case might make sense (we’ll also need to know to generate the accessor if .metal source files are detected in a framework, as that will result in a default.metallib resource that the code might want to access).
So you could generate that Bundle finding code for any .framework target that has "MACH_O_TYPE": "staticlib" set, if:
- Any resources are detected
- If
.metal source files are detected
- Maybe other situations I can’t think of where we can expect dynamic resource creation