Xcode version: 15.2, iOS deployment target 15.0
When experimenting with the new Xcode 15/new linker feature "Mergable Libraries" I came across an issue with subproject framework products.
I have a workspace with two projects.
I want to create a group framework named GroupFramework that consists of three other frameworks. FrameworkA, FrameworkB and FrameworkC.
FrameworkA and FrameworkB are in the same project as the group framework I want to create. FrameworkC is a product of another project in my workspace.
GroupFramework has all three frameworks linked in the "link binary with libraries" build phase.
GroupFramework has "Create merged binary" set to manual because I want to control which frameworks are linked. FrameworkA, FrameworkB and FrameworkC all have "Build mergeabe library" set to YES.
The application builds in both RELEASE and DEBUG.
Running in DEBUG and RELEASE
When running the application on a device in DEBUG or RELEASE it crashes with a linker error because FrameworkC can't be found on disk.
When inspecting the app binary in RELEASE I can see that FrameworkA and FrameworkB are both in the ReexportedBinary folder, but FrameworkC is nowhere to be seen.
I have tried embedding FrameworkC explicitly in the settings of the GroupFramework essentially creating an umbrella framework, and that also does not fix the issue.
I have also tried embedding FrameworkC directly into the AppTarget that will consume GroupFramework. This solves the linking issue but a new issue regarding unresolved symbols then crops up.
It appears as though the Reexporting and Merging of subproject frameworks is not working compared to a product that exists in the same project or at least may require a lot more manual configuration which is not currently documented anywhere. If this is the case, it could be very problematic when trying to adopt mergeable libraries in multi-project workspaces.
Post
Replies
Boosts
Views
Activity
Xcode 15 introduced official support for static frameworks.
docs here
This is presumably because there's quite a bit over overlap with the mergeable libraries feature.
Static frameworks potentially give developers a nice option for bundling resources with a static library. This was previously only really viable if you explicitly packaged up a .bundle with your .a (static library) which could be cumbersome for both the creator and consumer of a library. Or you explicitly copied your framework resources into your main app binary when building your app.
The release notes for Xcode 15 state:
Embedding a static framework using a Copy Files build phase now removes the static archive from the framework when it is embedded in the target bundle.
When inspecting the app target on disk this appears to be the case. In fact the behaviour is the same as with a mergable in release mode whereby a "stub" binary exists with no symbols.
The release notes also state:
The COPY_RESOURCES_FROM_STATIC_FRAMEWORKS build setting, previously used in the legacy build system to extract and copy the resources from a static framework to the target bundle, no longer has any effect with the new build system as the entire framework is copied instead
This also appears to be the case when inspecting the files on disk as the frameworks appear to be present in the main app with their resources.
So far so good. The issue arises when trying to access the bundle associated with this framework. The documentation doesn't mention any special access requirements to it's assumed that standard bundle access APIs should work.
Using a class
let bundle = Bundle(for: InfoCoreMain.self)
This returns the main application bundle and therefore fails to find the correct asset. This can be rationalised by the fact that the InfoCoreMain class does now actually exist within the main app target binary due to being statically linked. It's worth noting however, that mergable libraries in release mode do seem to work in this way and can resolve the bundle fine*.
Using an explicit bundle identifier
let bundle = Bundle(identifier: "***.***.InfoCore")
Using an explicit bundle identifier returns nil even though we can see the framework on disk.
Using a path
Using a path to access the framework also doesn't work.
if let bundlePath = Bundle.main.path(forResource: "InfoCore", ofType: "framework") {
let bundle = Bundle(path: bundlePath)
// Now you can access resources in the framework bundle
} else {
// Framework bundle not found
}
All of the above is the same behaviour for release and debug builds.
I have tried this on Xcode 15.2 and 15.3
*Mergeable libraries do seem to have issues in debug mode. See this forum post.
https://forums.developer.apple.com/forums/thread/749818