Introduction

Swift Package Manager supports resource bundles, but accessing them at runtime requires using Bundle.module (generated by SPM) instead of Bundle.main. When the bundle path is resolved incorrectly, images, localization strings, and data files embedded in a Swift package return nil or fail to load. This is especially confusing because resources work in the package's test target but fail when the package is used as a dependency.

Symptoms

  • Bundle.module.url(forResource:) returns nil
  • Images in SPM package not found by UIImage(named:in:compatibleWith:)
  • Localized strings from package not loading
  • Resources work in package tests but not in consuming app
  • Bundle.main.path(forResource:) does not find package resources

Debug bundle: ``swift // In your package code print("Bundle.module: \(Bundle.module)") print("Bundle path: \(Bundle.module.bundlePath)") print("Bundle resources: \(Bundle.module.paths(forResourcesOfType: nil, inDirectory: nil))")

Common Causes

  • Using Bundle.main instead of Bundle.module in package code
  • Resources not declared in Package.swift resources array
  • Resource file in wrong directory relative to target
  • Bundle module not generated for the target
  • Resource copy rule does not match expected file type

Step-by-Step Fix

  1. 1.Declare resources correctly in Package.swift:
  2. 2.```swift
  3. 3.// swift-tools-version: 5.7
  4. 4.import PackageDescription

let package = Package( name: "MyUIComponents", platforms: [.iOS(.v15)], products: [ .library(name: "MyUIComponents", targets: ["MyUIComponents"]), ], targets: [ .target( name: "MyUIComponents", resources: [ // Copy resources as-is .process("Resources"),

// Or copy specific files .copy("Assets/logo.png"), .copy("Assets/colors.xcassets"),

// Or process for localization .process("Localizations"), ] ), .testTarget( name: "MyUIComponentsTests", resources: [.process("TestResources")] ), ] ) ```

  1. 1.**Use Bundle.module correctly in package code":
  2. 2.```swift
  3. 3.import SwiftUI

// WRONG - Bundle.main does not contain package resources let image = UIImage(named: "logo", in: Bundle.main, compatibleWith: nil)

// CORRECT - Bundle.module is generated by SPM for the target let image = UIImage(named: "logo", in: Bundle.module, compatibleWith: nil)

// Loading data files func loadJSON(filename: String) -> Data? { guard let url = Bundle.module.url(forResource: filename, withExtension: "json") else { return nil } return try? Data(contentsOf: url) }

// Loading localized strings func localizedString(key: String) -> String { NSLocalizedString(key, bundle: Bundle.module, comment: "") } ```

  1. 1.**Access resources from SwiftUI views in a package":
  2. 2.```swift
  3. 3.import SwiftUI

// Image from package resource bundle struct PackageLogo: View { var body: some View { Image("logo", bundle: Bundle.module) .resizable() .frame(width: 100, height: 100) } }

// Accessing color assets from xcassets in package struct ThemedView: View { var body: some View { Rectangle() .fill(Color("PrimaryColor", bundle: Bundle.module)) } }

// Loading a text file resource struct HelpView: View { private let helpText: String

init() { if let url = Bundle.module.url(forResource: "help", withExtension: "md"), let content = try? String(contentsOf: url) { helpText = content } else { helpText = "Help content not available." } }

var body: some View { ScrollView { Text(helpText) } } } ```

  1. 1.**Debug resource loading issues":
  2. 2.```swift
  3. 3.// List all resources in the bundle
  4. 4.func debugBundleResources() {
  5. 5.let bundle = Bundle.module
  6. 6.print("Bundle: \(bundle.bundlePath)")
  7. 7.print("Bundle identifier: \(bundle.bundleIdentifier ?? "nil")")

// List all resource paths let resourcePaths = bundle.paths(forResourcesOfType: nil, inDirectory: nil) print("Resource paths:") for path in resourcePaths { print(" \(path)") }

// Check for specific resource if let url = bundle.url(forResource: "logo", withExtension: "png") { print("Found logo at: \(url)") } else { print("Logo not found!") print("Available PNG files:") let pngPaths = bundle.paths(forResourcesOfType: "png", inDirectory: nil) print(pngPaths) } } ```

Prevention

  • Always use Bundle.module in SPM package code, never Bundle.main
  • Verify resource directory structure matches Package.swift declarations
  • Add resource loading tests to the package test target
  • Use .process() for files that need localization processing
  • Use .copy() for files that should be copied as-is
  • Check Bundle.module.paths(forResourcesOfType:) during development