How can an app's xib/storyboard render images from an asset catalog (which is a swift package resource)?
Asked Answered
M

0

6

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 and Assets.swift to MyPackage
  • 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:

  1. The compiled/linked binary contains two copies of Assets.xcassets. One in MyPackage and one that is copied to the App Bundle.
  2. In Xcode, Assets.xcassets appears twice in the project. One in MyPackage 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?

Mosenthal answered 3/2, 2022 at 6:44 Comment(3)
Any progress on this? It seems like a pretty annoying impediment to modularizing featuresTheodoratheodore
No progress yet. I'm still copying the asset catalog to my app bundleMosenthal
Thanks. I've moved on to just making outlets and setting everything in code while gradually trying to get rid of all my storyboards.Theodoratheodore

© 2022 - 2024 — McMap. All rights reserved.