Swift Package Manager
Swift
iOS Development
Asset Management
Programming Tutorial

How to include assets / resources in a Swift Package Manager library?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Swift Package Manager can bundle resources such as images, JSON files, audio, and localized strings directly inside a package target. The normal setup is to declare those resources in Package.swift, place the files inside the target directory, and load them through Bundle.module. This capability requires Swift tools version 5.3 or later.

Add Resources to the Target Definition

Resources belong to a target, not to the package globally. A minimal manifest looks like this:

swift
1// swift-tools-version:5.9
2import PackageDescription
3
4let package = Package(
5    name: "DesignKit",
6    products: [
7        .library(name: "DesignKit", targets: ["DesignKit"])
8    ],
9    targets: [
10        .target(
11            name: "DesignKit",
12            resources: [
13                .process("Resources")
14            ]
15        )
16    ]
17)

.process("Resources") tells SwiftPM to bundle the contents of that directory. If you need the files copied without processing, use .copy("Resources") instead.

Organize the Package Layout Clearly

A common directory structure is:

text
1Sources/
2  DesignKit/
3    DesignKit.swift
4    Resources/
5      Theme.json
6      Icons/
7        logo.png

Keeping resources under one folder inside the target makes the manifest simple and reduces path mistakes. The important rule is that the resources must live under the target directory so SwiftPM can associate them with that target.

Load Files with Bundle.module

At runtime, load package resources from Bundle.module, not from Bundle.main:

swift
1import Foundation
2
3public enum ThemeLoader {
4    public static func loadTheme() throws -> Data {
5        guard let url = Bundle.module.url(forResource: "Theme", withExtension: "json") else {
6            throw NSError(domain: "DesignKit", code: 1)
7        }
8
9        return try Data(contentsOf: url)
10    }
11}

This is the key runtime detail. The consuming app is not the owner of the package bundle, so Bundle.main is the wrong place to look.

Load Images from the Package Bundle

If the resource is an image, you can still use the package bundle directly. With UIKit, for example:

swift
1import UIKit
2
3public enum PackageImages {
4    public static func logo() -> UIImage? {
5        UIImage(named: "logo", in: .module, compatibleWith: nil)
6    }
7}

This keeps the lookup logic inside the package instead of forcing the app target to know how the package stores its resources.

Test Resource Access Explicitly

A package can compile while resource loading is still broken, so add a small test that actually opens a file:

swift
1import XCTest
2@testable import DesignKit
3
4final class ResourceTests: XCTestCase {
5    func testThemeExists() throws {
6        let data = try ThemeLoader.loadTheme()
7        XCTAssertFalse(data.isEmpty)
8    }
9}

That catches missing manifest entries, wrong filenames, and path mistakes much earlier.

Keep Resource Access Inside the Package

A good package API hides bundle lookup details from consumers. Instead of asking app code to locate package files manually, expose helper functions that return decoded data, images, or strings. That keeps the resource contract stable even if the internal package layout changes later.

Common Pitfalls

  • Using Bundle.main instead of Bundle.module. Package resources are not stored in the main app bundle.
  • Forgetting to declare resources in Package.swift. Files on disk are not bundled automatically.
  • Choosing .copy when you expected processing behavior, or .process when exact original layout mattered.
  • Placing resource files outside the target directory so SwiftPM does not associate them with the target.
  • Assuming a successful build proves resource loading works. Add runtime tests that actually fetch the files.

Summary

  • Declare package resources in the target definition inside Package.swift.
  • Store the files under the target directory, usually in a Resources folder.
  • Load them with Bundle.module.
  • Use .process or .copy based on how the files should be bundled.
  • Add tests that open a real bundled resource so configuration mistakes are caught quickly.

Course illustration
Course illustration

All Rights Reserved.