Swift Package's Resource Bundle not present in xcarchive (when framework using said package is archived)

Context

We build a closed source Framework containing a few dependencies which are imported using Cocoapods. The framework is then distributed as a pod and embedded into various internal and external projects.

I have developed a private Package (SPM) that is meant to be consumed by the Framework. It is imported to the Framework's Xcode workspace as a Swift Package Dependency (along with the other Cocoapods dependencies). The package contains resources (.xib, images, localizedStrings) which are all private - the package's public API only exposes a UIViewController that itself uses all these resources (package accesses resources through Bundle.module).

The Framework's Xcode project also contains a target (a test iOS App) for simply importing and running the framework. Everything works perfectly. The test app is able to run the framework (import Library) and when the framework loads the UIViewController from the Swift Package (which in turn loads all the resources) everything display without trouble.

Problem

The problem comes when I archive the library. The .bundle from the Swift Package doesn't get copied into the resulting xcarchive. This leads to a crash happening at runtime when we run our framework inside other apps.

Fatal error: unable to find bundle named SPMPackage_Module

My understanding

This crash happens because the app isn't able to find the bundle when calling Bundle.module. This led me to find that the bundle indeed wasn't anywhere to be found in the Framework.

Our script archives the library (xcodebuild archive ...) on both iphonesimulator and iphoneos platforms and then packages the result as a universal framework. I would expect the package's bundle to be part of the archive but it's not.

Workaround

I have found the bundle in the DerivedData folder:

DerivedData/Intermediates.noindex/ArchiveIntermediates/OurFramework/IntermediateBuildFilesPath/UninstalledProducts/iphoneos/SPMPackage_Module.bundle

There is also an alias to that file in there:

DerivedData/Intermediates.noindex/ArchiveIntermediates/OurFramework/BuildProductsPath/Release-iphoneos/SPMPackage_Module.bundle

If I take that bundle and copy it into the Framework, then everything runs fine.

Questions

  • Is this a bug or am I missing a step when archiving my library? (That would lead to the swift package's bundle not being copied into the resulting archive)
  • Any better workaround that what is mentioned above?

Thanks!

1 Like

This is a known issue. At this time, resources only get embedded into "top-level" products, such as an application.

The workaround you are mentioning sounds good to me.

1 Like

Thanks for the prompt answer, @NeoNacho. I was not able to find this was a know issue during my research.

I'll go ahead with my workaround and wait for a more appropriate way to handle this situation.

Any updates to this issue?

I'm experiencing a similar problem when archiving an iOS app. The app depends on a SPM defined target which has resources: .process("Resources"). This builds and runs in debug just fine but when it goes to archive it fails with:

error: /Users/vagrant/Library/Developer/Xcode/DerivedData/iOSComponents-gcakrdwwfderkscolnnesyedzmga/Build/Intermediates.noindex/ArchiveIntermediates/DesignSystemApp/BuildProductsPath/Release-iphoneos/AppCenter_AppCenterDistribute.bundle: No such file or directory (in target 'DesignSystemApp' from project 'iOSComponents')

Looking at the derived data directory it looks like all the bundles are there, but they're under /IntermediateBuildFilesPath/UninstalledProducts/

thanks for the tip, I was going mental trying to find a solution or workaround. I ended up adding a script to copy the bundles.

cp -R ${BUILT_PRODUCTS_DIR}/*.bundle ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/

1 Like

This was really useful @andras . I've added this as a custom build step for my framework target that consumes Swift Packages with resources:

cp -RL "${BUILT_PRODUCTS_DIR}"/*.bundle "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/"

This has 2 differences to yours:

  1. I've used the -L flag to cp. When doing an archive of the framework, the .bundles in BUILT_PRODUCTS_DIR are symbolic links instead of the actual bundles. This flag makes sure the destination of the link is copied, instead of the link itself.

  2. I've used UNLOCALIZED_RESOURCES_FOLDER_PATH as the destination path. For iOS (slim) frameworks, this is just the root of the framework. I think for macOS, it would be the Resources directory within the framework.