Difficulty Accessing Resources in a Package's Bundle

Based on the docs, I tried to retrieve the URL of some resources in my package, but it seems to be impossible to do so ...

Doesn't matter if I use Bundle.module or Bundle.main to retrieve the URL of a file in a target of my package. In both cases, url(forResource: String, withExtension: String?) returned nil.

You can try the step by step guid as explained in docs in any package that you'd like, but you'll probably bump into the same issue as I did.

When debugging the issue, I found out that the modules of my package are not loaded:

public extension Bundle {
    static let distortionEffect: Bundle = .module
}
...
    Bundle.distortionEffect.load()
    print(Bundle.distortionEffect.isLoaded)  // Always is false

I'm not sure if it's related to the issue that I'm facing, but it's worth noting that I couldn't get the isLoaded property to be true.

I've been successfully using package resources for years but there has been some hurdles, here's a couple things off the top of my head:

Pretty sure you got the basics since you're following the docs and your Bundle.module gets generated.

  • Metal librairies have a different name because they get processed, I think the extension changes to metallib
  • Same with CoreData models, I don't remember their extension
  • When localization is involved I've had issues with locale directory placement
  • When resource generation is involved I've sometimes had issues with resources being placed outside the bundle
  • Localized resource generation I've never managed to get my specific case working

I think the isLoaded property is meant for dynamic libraries within the bundle, like extensions. I don't remember where I saw that though so don't quote me on that!

1 Like

Could you provide an example of how you have utilized package resources in your projects, either within the package itself or outside of it?

I couldn't use a single package resource with any kind of format ... I tried the settings.plist as explained in docs, metallib as you explained and even files without an extension and none of them worked.

let settingsURL = Bundle.module.url(forResource: "settings", withExtension: "plist")

Examples like this simply would not work for any resource inside of a module.

Here’s an example. SwiftCSV uses sample CSV files in its tests.

You’ll notice that Xcode and SwiftPM treat resources slightly differently.

I tried with my swift-raylib-examples, there's a couple of them with resources, I've added print statements for Bundle.module.url.

Here's one that works for me with Xcode without any special casing:

import RaylibKit
import Foundation

@main struct LogoRaylib: Applet {
	let logo: Texture
	
	init() throws {
		Window.create(800, by: 450, title: "Example - Textures - Logo Raylib")
		Application.target(fps: 60)
		
		print(Bundle.module.url(forResource: "logo", withExtension: "png"))
		logo = try Texture(at: "logo.png", bundle: .module)
	}
	
	func draw() {
		Renderer2D.texture(logo, at: Window.size / 2 - logo.size / 2)
		Renderer2D.text("this IS a texture!", at: 360, 370, size: 10, color: .gray)
	}
}

It successfully prints file:///Users/lancelot/Library/Developer/Xcode/DerivedData/raylib-examples-cbnjvpbaegxsqhadrivvnvswwhbn/Build/Products/Debug/swift-raylib-examples_Textures%20-%20Logo%20Raylib.bundle/Contents/Resources/logo.png.

I tried with your package, here's a pro-tip for debugging resources: in Xcode use Product > Show Build Folder in Finder then navigate to Products/Debug and you'll be able to see all of the generated bundles.

Something I forgot about Metal libraries, they're all pre-compiled and merged into a per-module library named default.metallib so individual .metal (or .metallib) files don't exist.

I fixed your color distortion test:

/// Always succeeds!
func testBundleSourceString() throws {
	let url = Bundle.colorEffect.url(forResource: "default", withExtension: "metallib")
	XCTAssertNotNil(url, "Failed to load ColorEffect metal library")
	if let url {
		print(url)
	}
}

You can also access the library via makeDefaultLibrary(bundle:) if you need it outside SwiftUI:

1 Like

That work-around is no longer necessary, I've cloned your package and replaced ResourceHelper.url with Bundle.module.url, deleted ResourceHelper.swift and the entire test suite built and all tests succeed.

2 Likes