Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xcode8: Usage of image literals in frameworks

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?

like image 464
dirtydanee Avatar asked Jan 24 '17 17:01

dirtydanee


1 Answers

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!

like image 107
diegomontoyas Avatar answered Oct 23 '22 16:10

diegomontoyas