Swift 5.3 SwiftPM Resources in tests uses wrong bundle path?

I played around with this a bit yesterday, but have only found a workaround for the case where resources are needed in a test target:

  1. Create a new executable target (one with a main.swift file) e.g. ResourcesPathProvider containing the resources.
  2. Put the following code in the main.swift file:
import class Foundation.Bundle
print(Bundle.module.bundlePath, terminator: "")
  1. Add an extension on Bundle in the target where you need the resources:
import Foundation
@testable import ResourcesPathProvider

extension Bundle {
    private static var productsDirectory: URL {
        #if os(macOS)
        if let bundle = Bundle.allBundles.first(where: { $0.bundlePath.hasSuffix(".xctest") }) {
            return bundle.bundleURL.deletingLastPathComponent()
        }
        fatalError("Couldn't find the products directory")
        #else
        return Bundle.main.bundleURL
        #endif
    }

    static var module: Bundle = {
        do {
            let process = Process()
            process.executableURL = productsDirectory.appendingPathComponent("ResourcesPathProvider")
            let pipe = Pipe()
            process.standardOutput = pipe

            try process.run()
            process.waitUntilExit()

            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            let output = String(decoding: data, as: UTF8.self)
            guard let bundle = Bundle(path: output) else {
                fatalError("Couldn't load module bundle from provided path: \(output)")
            }
            return bundle
        } catch {
            fatalError("Couldn't load module bundle: \(error)")
        }
    }()
}
  1. Use Bundle.module :slight_smile:
    Note that I intentionally used module as the name for the accessor, so that once Swift fixes this, I can simply move the resources to the test target, remove the executable target and the extension.

The code in the extension is a slightly adapted version of the one SPM automatically generates for tests of new executable packages. It basically just searches the executable and executes it. Given that the executable simply prints the path to its resources bundle, we can use the output and load the bundle. The upside is, that this seems to be rather safe and does not rely on many build system intrinsics, except for the part where we're looking for the executable. However, given that SPM ships with that code, I think it's rather safe to rely on it for now. At least until some version of Swift finally fixes this issue.

However, this only works for cases where the resources are needed in tests. For testing targets that themselves have resources, I have yet to find a workaround.