I'm trying to use a color set from a xcassets
folder that is inside a Swift Package (src). It doesn't seem to be working. I tested this out by writing a simple view that attempts to make use of the color:
Text("Hello")
.foregroundColor(Color("brandPrimary")
I got an empty view from this code; the text can't find the color.
I've done significant research around the web - reviewing WWDC videos such as Swift packages: Resources and localization, and they seem to suggest that xcassets
folder are automatically included as resources. It doesn't work for me.
I tried to add process("Resources/Colors.xcassets")
inside my package manifest, but that didn't help either.
From the Color
documentation:
init(_ name: String, bundle: Bundle? = nil)
…
bundle
The bundle in which to search for the color resource. If you don’t indicate a bundle, the initializer looks in your app’s main bundle by default.
Your GoodPackage library's assets are not the app's main bundle, so you need to tell the Color
initializer which bundle to search.
The Swift package manager's build process automatically creates a Bundle
for each target/module that contains assets. Within the module, you can access that generated Bundle
using the expression Bundle.module
. SwiftPM actually writes Swift code to a file named resource_bundle_accessor.swift
in your DerivedData to make this work. The generated source code looks like this:
import class Foundation.Bundle
import class Foundation.ProcessInfo
import struct Foundation.URL
private class BundleFinder {}
extension Foundation.Bundle {
/// Returns the resource bundle associated with the current Swift module.
static let module: Bundle = {
let bundleName = "YourPackageName_YourTargetName"
let overrides: [URL]
#if DEBUG
if let override = ProcessInfo.processInfo.environment["PACKAGE_RESOURCE_BUNDLE_URL"] {
overrides = [URL(fileURLWithPath: override)]
} else {
overrides = []
}
#else
overrides = []
#endif
let candidates = overrides + [
// 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,
]
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 YourPackageName_YourTargetName")
}()
}
As you can see, that static let module
property has (default) internal access, so you can only use it from source code within that module. By default, there is no way to access the module's Bundle
from outside the module.
One solution is to add a public accessor for the module Bundle
in the GoodPackage module. For example, add this to a source file in the GoodPackage module:
import Foundation
extension Bundle {
public var GoodPackage: Bundle { Bundle.module }
}
Then, in your app:
import GoodPackage
...
Text("Hello")
.foregroundColor(Color("brandPrimary", bundle: .GoodPackage))