Xcode / SPM integration issue—Xcode bundles frameworks into Swift Package frameworks that shouldn't have them

Xcode 12.5 added the ability for Xcode to automatically decide whether to use .dynamic or .static for the product type for a given Swift package when it builds it, based on whether it's used in multiple places or only in one place.

So I tried removing .dynamic throughout our app's 50+ local Swift packages. Afterwards, the app builds and runs fine.

However when trying to submit to the app store (test flight upload), we are getting rejected due to the fact that Xcode has now bundled every package dependency of a package into that package's framework's Frameworks folder, such that now there are 3, 4, and even 5 copies of each Swift package build product bundled in the app, nested inside of all the other frameworks and packages.

So for example, with a PackageA that's depended on by PackageB, that's depended on by PackageC, etc., previously when we manually specified ".dynamic," the app would have a directory structure like this:

App/
    Frameworks/
        PackageA.framework/
        PackageB.framework
        PackageC.framework
        XcodeProjectA.framework
        XcodeProjectB.framework
        XcodeProjectC.framework

However after removing "type: .dynamic" and letting Xcode infer, now Xcode builds an app with the following structure which gets rejected by the App Store for having duplicate bundles and Framework folders where they don't belong:

App/
    Frameworks/
        PackageA_3357E3B0B2AD110B_PackageProduct.framework/
        PackageB_AF8320FD902BA919_PackageProduct.framework/
            Frameworks/
                PackageA_3357E3B0B2AD110B_PackageProduct.framework/
        PackageC_182819ADFA392304_PackageProduct.framework/
            Frameworks/
                PackageA_3357E3B0B2AD110B_PackageProduct.framework/
                PackageB_AF8320FD902BA919_PackageProduct.framework/
        XcodeProjectA.framework
            Frameworks/
                PackageA_3357E3B0B2AD110B_PackageProduct.framework/
                PackageB_AF8320FD902BA919_PackageProduct.framework/
                PackageC_182819ADFA392304_PackageProduct.framework/
        XcodeProjectB.framework
            Frameworks/
                PackageA_3357E3B0B2AD110B_PackageProduct.framework/
        XcodeProjectC.framework
            Frameworks/
                PackageA_3357E3B0B2AD110B_PackageProduct.framework/
                PackageB_AF8320FD902BA919_PackageProduct.framework/
                PackageC_182819ADFA392304_PackageProduct.framework/

etc. etc. ... leading to there being many copies of the same framework embedded in the app.

However we don't have any "embed frameworks" build phases that should be responsible for this happening. And I don't see any way to force or compel Xcode not to bundle all these extra frameworks into the app.

I realize this isn't an Xcode support forum, but also, I don't know whether this is an Xcode bug or an SPM bug. Either way, I'm hoping maybe someone here knows why this would be happening, and if so, how to fix it.

BTW I did google extensively to try to find a solution, but no dice. I'm guessing my org is the only one actually trying to use SPM for locally-declared projects like this?

... Thanks...

6 Likes

You’re definitely not the only one. We are struggling with the same thing in our setup as well. Using Xcode 13 doesn’t seem to help either, it’s the same behavior you described.

1 Like

Create a workspace like this:

Workspace/
    App/
    AppFrameworks/
      Package.swift

The App should only depend on the AppFramework with all dependencies listed in Package.swift. Make sure the targets within AppFramework are static. This somehow forces XCode to eliminate all of the duplicates.

We are facing a very similar problem but with Extension targets.
Despite having "Do not embed" configured, Xcode embeds frameworks built from Swift packages into extension bundles.

Our setup is as follows:

  • :file_folder: Workspace
    • :hammer: App Project
      • :dart: App Target
      • :dart: Extension Target
    • :toolbox: Framework Project
    • :package: Swift Package (libaryType: .dynamic)

The app target uses the framework ("Link and Embed") and the Swift Package ("Link and Embed"). It also embeds the extension.
The extension uses the Framework ("Do not Embed") and the Swift Package ("Do not Embed").
The framework uses the Swift Package ("Do not Embed").
The Swift Package is a "leaf" dependency.

When building and running the app target, everything works as expected but when trying to upload the app to App Store Connect, we are get multiple ITMS errors referring to the extension. (ITMS-90206: "Invalid Bundle. The bundle contains invalid file 'Framework'", ITMS-90205: "Invalid Bundle. The bundle contains disallowed nested frameworks.", ITMS-90685: "CFBundleIdentifier Collision")

When inspecting the Contents of our .appex, it contains a .framework bundle under /Frameworks.
I double checked all involved build phases to ensure that no "Copy" phase is placing the SPM framework in anything except the main App Target.
When investigating the build output, I can find the (unwanted) embed step:

PBXCp /BuildProductsPath/Release-iphoneos/SPMBased.framework /UninstalledProducts/iphoneos/Extension.appex/Frameworks/SPMBased.framework (in target 'Intents Extension' from project 'for iOS')
    cd ~/Projects/App
    builtin-copy -resolve-src-symlinks /BuildProductsPath/Release-iphoneos/SPMBased.framework /UninstalledProducts/iphoneos/Extension.appex/Frameworks

By manually removing the /Frameworks folder, the ITMS errors go away - but this doesn't sound like the correct solution to this problem. Does anyone have a more elegant workaround? Or an idea why Xcode thinks it has to embed the SPM package products into the extension?

This would lead to duplicate symbol errors when statically linking the Swift package in e.g. an app target that links a framework which also (statically) links the same Swift package.

2 Likes

It's hard to believe that Apple lets this kind of thing into production software.

3 Likes

We are facing the same issue in our project. We have a spm-integration branch but can't merge because we can't upload on TestFlight. We stick with Cocoapods for the time being but will be nice to know more about this

I try a workaround likes this:

Local Package <- one library product with N targets
Framework <- Local Package [static] (Do not embed)
App <- Framework (Embed & Sign)
Extension <- Framework (Do not embed)

The Xcode warning ITMS-90685: "CFBundleIdentifier Collision". Set the DISABLE_DIAMOND_PROBLEM_DIAGNOSTIC to YES fix it.

And I get the same result from weichsel. The app warning this when launching :

objc[33528]: ### is implemented in both ### (0x108c05868) and ### (0x103acd578). One of the two will be used. Which one is undefined.

However, the app could be uploaded to App Store.


I have seen lots of discussions in the forum about this issue. But The Xcode 13.2 RC still does not fix it.

1 Like

To get around this issue, I run a custom script as the last build phase in my app project - as reported in Swift Packages in multiple targets results in “This will result in duplication of library code.” errors - #67 by pewe

3 Likes