About
Our Asset catalog was moved from the App target to swift package resource. Afterward the App's existing storyboard/xibs can no longer render those assets unless the asset catalog is copied via Copy Bundle Resources
phase (which means 2 copies in the compiled app). Is there a better way?
Consider the workspace before and after the asset catalog was migrated.
Before Migrating Assets.xcasset
- FooApp/
- FooApp/
- Resources/
- Assets.xcasset // #1.1 Standard asset catalog in app target
- Assets.swift // internal scope access to images in Assets.xcasset (swiftgen)
- ViewController/
- MyViewController.swift
- MyViewController.storyboard // #1.2 Contains a view which renders "imageA" from Assets.xcasset
- View/
- MyView.xib // #1.3 xib renders "imageB" from Assets.xcasset
- MyUIKitView.swift // Programmatically renders "imageC" from Assets.xcasset
- MySwiftUIView.swift // Programmatically renders "imageD" from Assets.xcasset
App works as expected. Typical asset catalog set up here. Images are consumed in a number of ways:
- Programmatically (UIKit & SwiftUI)
- Visually (xib & storyboard)
After (Moved Assets.xcassets to MyPackage)
- FooApp/
- FooApp/
- Resources/
- // #2.1 Assets.xcasset was relocated to MyPackage
- ViewController/
- MyViewController.swift
- MyViewController.storyboard // #2.2 Contains a view which renders "imageA" from Assets.xcasset
- View/
- MyView.xib // Contains a subview which renders "imageB" from Assets.xcasset
- MyUIKitView.swift // Programmatically renders "imageC" from Assets.xcasset
- MySwiftUIView.swift // Programmatically renders "imageD" from Assets.xcasset
- MyPackage/
- Package.swift // calls `.process("Resources/Assets.xcassets")`
- Sources/
- MyPackage/
- Resources/
- Assets.xcassets // Same asset catalog from #Before
- Assets.swift // public scope access to images in Assets.xcasset (swiftgen)
- Create a new swift package (MyPackage)
- Move
Assets.xcassets
andAssets.swift
toMyPackage
- In
MyPackage/Package.swift
, call.process("Resources/Assets.xcassets")
- Change our programmatic image accessors to
public
scope
Here is where we start to run into issues.
The problem
- [Pass]
MyPackage
and the App both compiles without warning/error. Links, and launches just fine - [Pass] At run time, any programmatic references to images render as expected.
- [FAIL] At run time, any presented xib/storyboard files fail to render the images.
- [Note] If I inspect the xib/storyboard in Xcode, that the image renders.
- [Note] If I click on the image (as if I were to select another), the popup list correctly renders all of the images contained in
Assets.xcassets
.
So, Xcode knows enough about the images to render them, and to compile without warnings or errors, but the images are missing at runtime.
A Solution (with side effects)
After poking around I arrived at a solution which works (but one that I am dissatisfied with):
In the app's Build Phase settings I added an item which copies MyPackage/Sources/MyPackage/Resources/Assets.xcassets
After compiling/running again, the images render correctly for storyboards/xibs. Nice, but there are some undesirable side effects:
- The compiled/linked binary contains two copies of
Assets.xcassets
. One inMyPackage
and one that is copied to the App Bundle. - In Xcode,
Assets.xcassets
appears twice in the project. One inMyPackage
and another in the root of the app source. It cannot be moved or hidden without breaking the copy phase. They are symbolic linked at least so editing IS editing the other.
Alternatives?
It's time to ask for help. I assume that this is not the best / intended way. I have exhausted my google-fu. Couldn't find specifics in Apple's docs.
I'm imagining that there is a more customized way to do the copy step (maybe excluding the asset catalog in Package.swift)?. Or possibly a way to compile the asset catalog in Build Rules tab?
Or am I doing it the right way?