Summary
CodeEditSymbols builds cleanly under Xcode but has two problems for consumers
who build with swift build on the command line and package their own .app:
- The build fails outright.
Package.swift never declares
Symbols.xcassets as a resource, so SwiftPM's command-line build does not
process the asset catalog and does not synthesize Bundle.module. (Xcode
auto-detects asset catalogs, which is why this is invisible in an Xcode
build.) This is a one-line manifest fix.
- Once building, a distributed
.app crashes on first symbol access,
because the generated Bundle.module accessor cannot find the resource
bundle inside a .app.
Environment
- Package:
CodeEditSymbols 0.2.3.
- Build:
swift build from the command line. No .xcodeproj. The app is
assembled into a .app by a custom script that copies the SwiftPM resource
bundle (CodeEditSymbols_CodeEditSymbols.bundle) into
MyApp.app/Contents/Resources/.
- macOS, Apple Silicon, current Swift toolchain.
Defect 1 — swift build fails: asset catalog not declared as a resource
Root cause
Package.swift declares the target with no resources: rule:
.target(
name: "CodeEditSymbols",
dependencies: []
),
Sources/CodeEditSymbols/Symbols.xcassets is therefore not treated as a
package resource by the SwiftPM command-line build, Bundle.module is not
generated, and CodeEditSymbols.swift (which references Bundle.module)
fails to compile / the resource is never processed. Xcode papers over this by
auto-detecting .xcassets; swift build does not.
Repro
-
swift build a target that depends on CodeEditSymbols (no Xcode project).
-
Build fails:
Sources/CodeEditSymbols/CodeEditSymbols.swift:47:16: error: type 'Bundle' has no member 'module'
- Expected:
swift build succeeds and bundles the symbol assets.
- Actual: build failure.
Fix
Declare the asset catalog as a processed resource. .process has been
available since swift-tools-version 5.3, so no tools-version bump is needed
(the manifest stays at 5.5):
.target(
name: "CodeEditSymbols",
dependencies: [],
resources: [
.process("Symbols.xcassets")
]
),
Defect 2 — Bundle.module fatalErrors in a distributed .app
Root cause
Same shape as the companion CodeEditLanguages issue. The SwiftPM-generated
accessor probes only Bundle.main.bundleURL/<name>.bundle (the .app wrapper
root) and a hardcoded absolute build-machine path, then fatalErrors.
Contents/Resources/<name>.bundle — where a CLI-assembled app copies the
resource bundle — is not among the probed locations.
Bundle.module is used in CodeEditSymbols.swift:
init(symbol: String) {
self.init(symbol, bundle: Bundle.module) // Image
}
static func symbol(named: String) -> NSImage? {
Bundle.module.image(forResource: named) // NSImage
}
Repro
- With Defect 1 fixed, assemble a
.app and copy
CodeEditSymbols_CodeEditSymbols.bundle into Contents/Resources/.
- Run the
.app; access any Image(symbol:) / .symbol(named:).
- Crash at the accessor's
fatalError.
- Expected: the symbol renders; no crash.
- Actual:
fatalError("could not load resource bundle …").
Fix
Resolve the bundle from Bundle.main.resourceURL (i.e. Contents/Resources)
first, then fall back to Bundle.module (which still serves swift build /
swift test on the build machine). This changes nothing for Xcode consumers:
let symbolsBundle: Bundle = {
if let bundled = Bundle.main.resourceURL?
.appendingPathComponent("CodeEditSymbols_CodeEditSymbols.bundle"),
let bundle = Bundle(url: bundled) {
return bundle
}
return Bundle.module
}()
…and route the two Bundle.module references through symbolsBundle.
Offer to contribute
We're already running both changes as a local patch against 0.2.3 with no
observed regression. Per the CodeEdit contribution convention (open an issue
first, ask to be assigned), we'd be happy to submit the PR — could you assign
this to us? The patch passes SwiftLint. Glad to adjust if you'd prefer a
different approach.
Thanks for maintaining CodeEditSymbols.
Summary
CodeEditSymbolsbuilds cleanly under Xcode but has two problems for consumerswho build with
swift buildon the command line and package their own.app:Package.swiftnever declaresSymbols.xcassetsas a resource, so SwiftPM's command-line build does notprocess the asset catalog and does not synthesize
Bundle.module. (Xcodeauto-detects asset catalogs, which is why this is invisible in an Xcode
build.) This is a one-line manifest fix.
.appcrashes on first symbol access,because the generated
Bundle.moduleaccessor cannot find the resourcebundle inside a
.app.Environment
CodeEditSymbols0.2.3.swift buildfrom the command line. No.xcodeproj. The app isassembled into a
.appby a custom script that copies the SwiftPM resourcebundle (
CodeEditSymbols_CodeEditSymbols.bundle) intoMyApp.app/Contents/Resources/.Defect 1 —
swift buildfails: asset catalog not declared as a resourceRoot cause
Package.swiftdeclares the target with noresources:rule:Sources/CodeEditSymbols/Symbols.xcassetsis therefore not treated as apackage resource by the SwiftPM command-line build,
Bundle.moduleis notgenerated, and
CodeEditSymbols.swift(which referencesBundle.module)fails to compile / the resource is never processed. Xcode papers over this by
auto-detecting
.xcassets;swift builddoes not.Repro
swift builda target that depends onCodeEditSymbols(no Xcode project).Build fails:
swift buildsucceeds and bundles the symbol assets.Fix
Declare the asset catalog as a processed resource.
.processhas beenavailable since swift-tools-version 5.3, so no tools-version bump is needed
(the manifest stays at 5.5):
Defect 2 —
Bundle.modulefatalErrors in a distributed.appRoot cause
Same shape as the companion
CodeEditLanguagesissue. The SwiftPM-generatedaccessor probes only
Bundle.main.bundleURL/<name>.bundle(the.appwrapperroot) and a hardcoded absolute build-machine path, then
fatalErrors.Contents/Resources/<name>.bundle— where a CLI-assembled app copies theresource bundle — is not among the probed locations.
Bundle.moduleis used inCodeEditSymbols.swift:Repro
.appand copyCodeEditSymbols_CodeEditSymbols.bundleintoContents/Resources/..app; access anyImage(symbol:)/.symbol(named:).fatalError.fatalError("could not load resource bundle …").Fix
Resolve the bundle from
Bundle.main.resourceURL(i.e.Contents/Resources)first, then fall back to
Bundle.module(which still servesswift build/swift teston the build machine). This changes nothing for Xcode consumers:…and route the two
Bundle.modulereferences throughsymbolsBundle.Offer to contribute
We're already running both changes as a local patch against 0.2.3 with no
observed regression. Per the CodeEdit contribution convention (open an issue
first, ask to be assigned), we'd be happy to submit the PR — could you assign
this to us? The patch passes SwiftLint. Glad to adjust if you'd prefer a
different approach.
Thanks for maintaining CodeEditSymbols.