I am currently updating a project to use image literals
, to enjoy the benefits of non-optional images. The project is importing various frameworks, and the frameworks are containing images.
In the frameworks, we had to declare an extension on UIImage
to override the initialiser, because it was looking for the image in the wrong bundle. We did something like:
extension UIImage {
convenience init?(framework_named imageName: String) {
let bundle = Bundle(for: ClassNameInFramework.self)
self.init(named: imageName, in: bundle, compatibleWith: nil)
}
}
I am wanting to use image literals in the framework too, but like before, the literals are looking for the wrong bundle for the image and the application crashes.
Does anyone know, how to specify the image literal to which bundle to look for the image?
I discovered a pretty simple workaround and was amazed to not find it anywhere on other posts. I wish it could be more elegant, but it's preferable than using the stringly-typed UIImage(named:in:compatibleWith:)
initializer in my opinion.
We basically take advantage of the _ExpressibleByImageLiteral
protocol, which is what Xcode uses to determine if a type is expressible by an image literal. It's part of the Swift standard library, but it's hidden from autocompletion, I guess because it's uncommon to want to initialize a custom type with an image literal. However, it's just what we want and behaves exactly like the other ExpressibleBy
protocols.
struct WrappedBundleImage: _ExpressibleByImageLiteral {
let image: UIImage?
init(imageLiteralResourceName name: String) {
let bundle = Bundle(for: ClassInFramework.self)
image = UIImage(named: name, in: bundle, compatibleWith: nil)
}
}
Note that I use a wrapper instead of a UIImage
subclass, which would seem like a better option. Sadly, classes like UIImage
are not intended to be subclassed and you will find yourself getting a lot of headaches if you do.
And now its usage becomes:
let image = (🏞 as WrappedBundleImage).image
Not as concise as a normal image literal, but it's the best we've got for now. We just have to remember to do the as
casting, otherwise our custom initializer will not get called.
You can also do something like this:
extension UIImage {
static func fromWrappedBundleImage(_ wrappedImage: WrappedBundleImage) -> UIImage? {
return wrappedImage.image
}
}
And we can now use it like this:
UIImage.fromWrappedBundleImage(🏞)
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With