Swift 5.3 SPM Resources in tests uses wrong bundle path?

I had a similar issue in my project as well. I have a Swift Package with Resources folder that contains fonts and asset catalogue,

Style/
  - Source/
    - Color.swift
    - Resources/
      - Processed/
        - Colors.xcassets
        - Fonts/
          - MyFont.ttf

And this module is embedded in my Main.app. One of my unit test target CalendarTests.xctest was accessing the apis of Style module. So when I ran the the CalendarTests.xctest it was crashing here,

fatalError("unable to find bundle named Style_Style")

However when I run the Main app everything works fine, but only during the test run, I see this crash.

After digging through various build directories inside DerivedData, I noticed that my CalendarTests.xctest contains a copy of Style_Style.bundle, but the auto generated resource_bundle_accessor.swift is not searching inside the "currently executing" test bundle, in this case CalendarTests.xctest/Style_Style.bundle. The auto generated code looks for Bundle.main and Style module's bundle(i.e PackageFrameworks/Style.framework/), but not the bundle that is "currently executing".

In order to circumvent this problem, I made my own "bundle search" logic instead of relying on Bundle.module property, which seems to fixed this crash.

This is my custom bundle search code,

extension Foundation.Bundle {
    /// Returns the resource bundle associated with the current Swift module.
    static var styleModule: Bundle = {
        let bundleName = "Style_Style"

        let candidates = [
            // Bundle should be present here when the package is linked into an App.
            Bundle.main.resourceURL,

            // Bundle should be present here when the package is linked into a framework.
            Bundle(for: BundleFinder.self).resourceURL,

            // For command-line tools.
            Bundle.main.bundleURL,
        ] + Bundle.allBundles.map { $0.bundleURL }

        for candidate in candidates {
            let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
            if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
                return bundle
            }
        }
        fatalError("unable to find bundle named Style_Style")
    }()
}

The code looks identical to the auto generated code, except that I have additionally included Bundle.allBundles.map { $0.bundleURL } to candidates array.

All the classes in my Style module now uses the new Bundle.styleModule property, instead of Bundle.module.

The next thing Im gonna do is to create a macro as @SDGGiesbrecht suggested.

var candidates = [ ... ]

#if TESTING
    candidates += Bundle.allBundles.map { $0.bundleURL }
#endif

...

Im not sure if this is a clean solution, but at least it gives a workaround solution while waiting for SR-13714.

1 Like

Does Xcode 12.5 fix this?

Also does Apple have any plans to support Swift Packages being used as a replacement for Xcode project files? Because right now, every time we try to convert an Xcode project into a package, we run into yet another bug in the SPM-Xcode integration.

As of Xcode 12.5 Beta 3, no.

Won't find out until a future WWDC. Apple is very tight-lipped about it's proprietary products. Also, from an Xcode point of view, it has to support multiple languages, Interface Builder and other tools, Xcode Server, and a bunch of other stuff that is rapidly going to be at odds with open-source SPM.

1 Like

Also does Apple have any plans to support Swift Packages being used as a replacement for Xcode project files? Because right now, every time we try to convert an Xcode project into a package, we run into yet another bug in the SPM-Xcode integration.

One can dream.

Its nice not getting merge conflicts every time someone changes a file.

It seems that this is fixed in Xcode 12.5 RC - using .module has correctly been finding the bundle during unit tests.

1 Like

I'm afraid this issue is still not fixed in Xcode 12.5 RC. My project is still crashing in 12.5. I have public computed property that returns the .module from my swift package.

// Style Package
public extension Bundle {
    @objc
    static var styleBundle: Bundle { .module }
}

Im referring the that bundle from one of my Objective C module like below.

@import Style;

+ (void)load
{
    // Crashes here
    NSBundle *bundle = [NSBundle styleBundle];
}
1 Like
  • (void)load

Curious why are you using + (void)load here? "load" This method has specific behaviors that could be why you're seeing a crash. See:

https://developer.apple.com/documentation/objectivec/nsobject/1418815-load?language=objc

I created a feedback for it with a demo repo with the issue for apple: GitHub - simonpierreroy/bugSwiftPMResources

Terms of Service

Privacy Policy

Cookie Policy