How to return a custom object in a swift convenience initializer?

2.1k Views Asked by At

I'm trying to do something like this:

public extension UIImage {
    public convenience init(whatever: Int) {
        UIGraphicsBeginImageContextWithOptions(...)

        //...

        let image = UIGraphicsGetImageFromCurrentContext()
        UIGraphicsEndImageContext()

        return image // <- impossible
    }
}

But this is not possible as "nil" is the only valid return for an initializer... How do i do this?

For example, the Objtive-C method [UIImage imageNamed:] is a class method (that can return whatever it wants in Objective-C) and it was mapped to the swift initializer UIImage(named:).

2

There are 2 best solutions below

1
Nate Cook On

What you want is a class factory method, not an initializer. Most factory methods in Foundation/Cocoa are automatically bridged to initializers, but if what you want can't be done via init, you can add a new class method:

public extension UIImage {
    class func imageWithWhatever(whatever: Int) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(...)

        //...

        let image = UIGraphicsGetImageFromCurrentContext()
        UIGraphicsEndImageContext()

        return image
    }
}
4
Knight0fDragon On

This is because you are returning a new object, not self. The point of init is to create the structure of your object, not a new one, so if you want to do it as a convenience init, you need to do it like this:

public extension UIImage {
    public convenience init?(whatever: Int) {
        defer {
            UIGraphicsEndImageContext()
        }
        UIGraphicsBeginImageContextWithOptions(...)

        //...
        guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
        guard let image = currentContext.makeImage() else { return nil }

        self.init(cgImage:image)
    }
}

perhaps instead of a convenience init, you want to create a class function that is doing what you are asking:

public class func createImage(whatever: Int) -> UIImage? {
    defer {
        UIGraphicsEndImageContext()
    }
    UIGraphicsBeginImageContextWithOptions(...)

    //...
    guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
    guard let cgImage = currentContext.makeImage() else { return nil }
    let image = UIImage(cgImage: cgImage)

    return image
}

I apologize that this is not 100% to code, but that is basically the gist of it