NSImage from Asset Catalog in Test Target
Asked Answered
S

1

8

My unit tests target needs to load an image resource for use in some of the tests, but I am having trouble loading it.

  1. I have created an asset catalog within the test target.
  2. I have added a new image set resource to the catalog, checked the "Mac" box in "Devices", and set its source images for both resolutions (@1x and @2x):

enter image description here

  1. I have checked the "copy bundle resource" build phase of the test target, and it does include the asset catalog.

  2. I have checked the target membership of the asset catalog and image set, and it is indeed the test target.


When running the tests, I am attempting to load the image using code like below:

guard let image = NSImage(named: NSImage.Name("TestSourceImage")) else {
    fatalError("Test Resource is Missing.")
}

...but the guard fails.

I also tried:

let bundle = Bundle(for: MyClassTests.self)

guard let path = bundle.pathForImageResource(NSImage.Name("TestSourceImage")) else {
    fatalError("Test Resource is Missing.")
}

guard let image = NSImage(contentsOfFile: path) else {
    fatalError("Test Resource File is Corrupt.")
}

...but the first guard fails (can't retrieve the resource path).

I have tried both formats

NSImage.Name("TestSourceImage")

and:

NSImage.Name(rawValue: "TestSourceImage")

I have also tried Bundle.urlForImageResource(_), but it fails too.


I have seen similar questions and answers, but they either apply to iOS or to resources in the app (main) bundle.

What am I missing?


UPDATE:

In the meantine, I've worked around the problem by adding my test image as a stand-alone image resource (not using asset catalogs), and loading it with the following code:

let bundle = Bundle(for: MyClassTests.self)
guard let url = bundle.url(forResource: "TestSourceImage", withExtension: "png") else {
    fatalError("!!!")
}
guard let image = NSImage(contentsOf: url) else {
    fatalError("!!!")
}

I don't need to support multiple resolutions in this case (my image is the source for an image processing algorithm; it is already assumed to be at the highest resolution necessary), and even if I did, I would just switch from .png to .tiff, I guess.

The problem seems to be that, NSImage does not have an initializer analogous to UIImage's init(named:in:compatibleWith:), which lets you specify the bundle from which to load the image (second argument).

You can instead ask a specific bundle (other than main) to create a resource URL and instantiate the image from that (like I did above), but this isn't compatible with asset catalogs it seems. Further information is welcome...

Supplicant answered 22/3, 2018 at 5:50 Comment(0)
P
9

There is an extension on Bundle that allows to load a named image from an asset catalog:

extension Bundle {
    @available(OSX 10.7, *)
    open func image(forResource name: NSImage.Name) -> NSImage?
}

Referring to your example, you may use the following in an XCTestCase to access the image (Swift 5):

guard let image = Bundle(for: type(of: self)).image(forResource: "TestSourceImage") else {
    fatalError("Test Resource is Missing.")
}

Tested on macOS 10.14.

Pannikin answered 6/1, 2020 at 19:46 Comment(1)
Thank you, it works! It's been a while and I'm not working on that project at the moment (although it's not exactly abandoned), so I had to do some searching to find it and try your solution :-)Supplicant

© 2022 - 2024 — McMap. All rights reserved.