Swift 5.3 SPM Resources in tests uses wrong bundle path?

I think that’s a separate issue, but it might possibly be related.

The issue that PR addresses is where test targets with resources look in your bin folder for package resources instead of the build folder (or whatever folder you have set). And this only happens when you run the tests via the command line. When running in Xcode the tests run fine.

So the impact is mostly on CI/CD pipelines/Linux.

I tried on xcode 12.2 version but still facing the issue while running the test target.
Fatal error: unable to find bundle named: file Common/resource_bundle_accessor.swift, line 27

not sure if something else needs to be done

2 Likes

Any news on this issue? I just ran into it for the first time. Xcode 12.4 (12D4e).

2 Likes

It works fine when in development from Xcode to a device, but it crashes and shows Fatal error: unable to find bundle named: file Common/resource_bundle_accessor.swift, line 27 when exporting to App Store or to an .ipa install file. I used Xcode 12.4 and 12.5 beta.

Still the same on the Version 12.4 (12D4e)

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

I can't believe this. I just ran into the same problem on a different project, at a different company--and when I searched for it, I found my own comment from a year ago. I don't know what's worse--that I didn't remember having the problem before, or that Apple still hasn't fixed it. :man_facepalming:

1 Like

@NeoNacho @eskimo sorry for the shout, but maybe you know the best people that could provide either an update or some help… it may be that this is not believed as good practice (unit tests are meant not to use resources, it could be a good argument) and/or the intended use would be say with a test iOS app project that uses the SPM package in question (and thus you have a proper integration / E2E test), but I am not sure what the exact use cases are that trigger this and it feels a middle ground that is not unsupported or discouraged nor working (this thread dates back years).

1 Like

Unit tests should absolutely be able to use resources. For example, whenever I test API compliance, I save server responses as JSON files and inject those into mock servers when unit-testing.

In practice, what I see in many open-source projects is that they continue to do development (including unit testing) using their Xcode project files, and reserve the Package.swift file for others to integrate against. One easy way to tell this is to see whether Package.swift includes a testTarget().

2 Likes

I do not necessarily disagree but those would be integration tests more than unit tests which should not be hitting the network or the filesystem. Anyways, it does not change the heart of the problem in this thread and I hope it is addressed and/or acknowledged.

Wanted to migrate from Cocoapods to SPM, but we are hitting this and is blocking us.