Tuist generates a target for resources for a static framework. This is mostly fine, but I’m missing a feature where localizations are extracted by Xcode from SwiftUI views, and a Localizable.xcstrings file in the Resources folder is updated automatically.
This coupling is much more powerful in Xcode 26 where code can be updated from changes to the Localizable.xcstrings using the new “Refactor” option in xcstrings files.
However this does not work as the static frameworks themselves do not have any resources declared.
I can’t actually switch to normal frameworks where this would just work, because my views depend on TCA which is a native SPM dependency, and is built as a static library by SPM.
The separation between the resources and the source code seems to be the fundamental issue, as the automatic extraction feature requires them to coexist in the same target.
If I try to add the Localizable.xcstrings directly to the static framework, the localization sync succeeds and updates the xcstrings file.
If I use a normal framework and just set MACH_O_TYPE
to staticlib
instead of creating a static framework as per Tuist’s definition, everything works like I expect. I guess I’m not sure what the generated resource bundle’s point is and what it’s trying to solve?
EDIT: After more testing I noticed that if I have a normal framework that’s statically linked, A, that depends on a static framework that has a resources bundle, B, BundleFinder fails to find the bundle B’s resources and fatalErrors.
This seems to be because B’s resources get copied into the A’s bundle, and I can’t think of a way to find them there in general through something like BundleFinder.
We introduced the associated bundle a long time ago when static targets couldn’t contain resources. I believe this has changed, so we might need to revisit that. @marekfort might have more context around this, but I was thinking we can run some tests with vanilla Xcode projects and multiple platforms and graphs to confirm that libraries can contain resources now.
That’d be great. Yeah static frameworks are now officially supported since Xcode 15 it seems:
Yes, they are supported by Xcode. There was an effort to stop generating bundles for static frameworks with resources but we ultimately had to revert because it caused some unexpected regressions: Revert embedding static framework instead of generating a resource bundle by fortmarek · Pull Request #7232 · tuist/tuist · GitHub
Whoever picks that work up, most of the PR should be still possible to reuse and I believe there’s just one highlighted regression around Objective-C from that reversion that still needs to be addressed.
Maybe it’d be reasonable to merge this under some flag so people can utilize this behavior despite the regressions?
Because on my end none of the mentioned regressions seem to be relevant for my use case and I’d really like to avoid the bundle accessors.
I’m also having an issue with Bundle.module
not being available if I disable Tuist’s generated bundle accessors.
Do you guys happen to know what it takes for it to be generated for an Xcode target? I have resources specified in build phases but Xcode refuses to generate the .module
bundle.
I’ve experimented and learned a few things:
If I use a normal framework and change its link style to static library, resources do end up in it.
However I think Xcode won’t generate a module Bundle accessor for the framework, because code always ends up in the main bundle if it’s statically linked.
But the resources are still in the framework that’s embedded in the app bundle. If I do:
let frameworksURL = Bundle.main.privateFrameworksURL!
let frameworkBundleURL = frameworksURL.appendingPathComponent("MyFramework.framework")
let bundle = Bundle(url: frameworkBundleURL)!
From the framework code, I can find its resources that way if it’s embedded in an app bundle.
However in an xctest, just like in the BundleFinder code generated for Tuist’s static frameworks, you have to move up a level and find the bundle there.
So basically I’d really like to edit the generated Tuist BundleFinder accessors and add support for these use cases.
Now I realized I’m being silly and most of what I was looking for was in the linked PR, which I just sort of reproduced on my own, heh.
It looks like I can’t really get what I want without forking Tuist myself… Pretty frustrating.
Anyway… if you ended up bringing this feature back and putting it behind a legacy flag I’d be very grateful. I don’t know that I can do this myself as I’ve already spent way too much time on this stuff. I need to actually build my application.
@marekfort @pepicrft I want to add another use case that I’ve come across that the resources bundle messes up for me, and that’s working with Metal shaders in a framework.
Normally I’d declare the shaders as resources. The framework might define special Metal settings, such as including debug symbols in the built metallib
which are necessary for shader debugging.
However, with the resources bundle, the shaders get bundled up. Then when the app processes the bundle, it notices the shaders and compiles them with default settings (i.e no debug symbols), and then finally copies the compiled metallib
into the app bundle.
The effect of this is that it doesn’t matter what Metal build settings I use on the framework, they end up getting ignored.
My solution for this, since it’s just a single framework I have with shaders, was to use a normal framework with MACH_O_TYPE
of staticlib
, and write my own version of BundleFinder
that accounts for xctest
contexts etc.
This is really quite frustrating…
We definitely think removing the generated bundles for static frameworks with resources is a need, we just need time to get to that. If you are struggling with the current way it works, you’re more than welcome to contribute – we’re happy to give you pointers, etc 