Hive Hive
Sign in

Allow .staticFramework Bundle-Finding Logic To Be Used in .frameworks

GitHub issue · Open

Metadata
Source
tuist/tuist #8219
Updated
Jun 11, 2026
Domains
Generated projects
Details

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:

  1. Tuist either doesn’t always generate a Bundle accessor (such as in the Metal shaders case).
  2. 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:

  1. Any resources are detected
  2. If .metal source files are detected
  3. Maybe other situations I can’t think of where we can expect dynamic resource creation
Flights

Investigate, reproduce, or fix this item in an isolated repository. Each Flight preserves its outcome and agent session.

New Flights are paused Configure model inference, GitHub, and a sandbox provider to start another Flight. Existing results remain available below.
No Flights yet

Start a Flight and preserve its objective, outcome, and session here.

Comments
D
dosubot[bot] Sep 16, 2025

Tuist recently improved bundle accessor generation for frameworks and static frameworks: it now generates a bundle accessor if a target has resources, .metal source files, or buildable folders with resources—even if no explicit resources are declared. This aligns with SwiftPM behavior and helps with cases like Metal shaders, where a default.metallib needs to be found at runtime (PR #8125, PR #8156, PR #8158).

However, static frameworks in Tuist still do not support resource bundles, so Bundle.module is not generated for them—even if you use buildable folders or have .metal files. This is a known limitation due to both Tuist’s design and Xcode’s handling of static frameworks. If you need Bundle.module functionality, the only reliable workaround is to use a dynamic framework, or to avoid buildable folders and specify resources and sources explicitly via Target.sources and Target.resources (discussion).

Manual workarounds for resource access in static frameworks exist (like your custom bundle-finding code), but they’re not ideal. The Tuist team is aware of these limitations and is tracking ongoing fixes and improvements in the changelog and community threads.

To reply, just mention @dosu.


Docs are dead. Just use Dosu.

Leave Feedback Ask Dosu about tuist Share Dosu with your team

A
adarhef Sep 16, 2025
  1. I already use sources and resources, not buildable folders.
  2. I just validated this again, so to clarify: Tuist indeed still does not generate a bundle accessor for a framework target even if it contains Metal shader files. It only does so for staticFramework. And even if I use a staticFramework instead, the generated accessor doesn’t work when trying to fetch the default.metallib, since it’s not actually added to the generated bundle (it can’t be, because it doesn’t exist until the module is built).

But that’s beside the point, frankly. Even if it generated an accessor it would probably just be the normal dynamic framework accessor, which doesn’t help since I’m manually setting the MACH_O_TYPE to staticlib and so the normal accessor wouldn’t find the right bundle anyway.

A
adarhef Sep 17, 2025

Just noticed this PR https://github.com/tuist/tuist/pull/8207 If I understand this correctly, I can expect the default.metallib to be found in the generated bundle, which is great. Will save the bit of code in the Metal shaders case. Looking forward to testing this when a version with this PR is out.

But the suggestion still stands, would still like to see the code for finding the bundle for a normal framework linked statically that has normal resources such as xcstrings.

D
dosubot[bot] Sep 17, 2025

For statically linked frameworks (MACH_O_TYPE=staticlib) with resources like xcstrings, Tuist does not generate a standard Bundle.module accessor due to Xcode and Tuist limitations. However, you can use a bundle-finding pattern similar to what Tuist generates for static frameworks. Here’s a Swift example based on the current best practice:

extension Foundation.Bundle {
static let module: Bundle = {
final class BundleFinder {}
let bundleFinderResourceURL = Bundle(for: BundleFinder.self).resourceURL?.appendingPathComponent("..")
var candidates: [URL?] = [
Bundle.main.privateFrameworksURL,
bundleFinderResourceURL,
bundleFinderResourceURL?.appendingPathComponent("Frameworks"),
]
#if canImport(XCTest)
candidates.append(bundleFinderResourceURL?.appendingPathComponent(".."))
#endif
for candidate in candidates {
let frameworkURL = candidate?.appendingPathComponent("YourFrameworkName.framework")
if let bundle = frameworkURL.flatMap(Bundle.init(url:)) {
return bundle
}
}
fatalError("Unable to find static framework \"YourFrameworkName.framework\"")
}()
}

Replace "YourFrameworkName.framework" with your actual framework name. This code searches common locations for the embedded framework bundle, including test contexts, and returns the first match. This is the same pattern Tuist uses for static frameworks with resources like CoreData models or Metal shaders (reference).

For xcstrings, there are still caveats: Xcode’s string extraction works only if the .xcstrings file is added to the static library’s Copy Bundle Resources phase, even though it won’t be copied into a bundle at runtime. This workaround helps Xcode pick up the file for extraction, but runtime access still relies on the bundle-finding code above (reference).

There’s no official Tuist support for synthesized accessors for xcstrings in static frameworks yet, and this area is still a work in progress. If you need full Bundle.module-style access, switching to a dynamic framework is the only fully reliable solution for now (reference).

To reply, just mention @dosu.


Docs are dead. Just use Dosu.

Leave Feedback Ask Dosu about tuist Share Dosu with your team