Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling class initialization failure in Swift extension

I'm rewriting an Objective C category below to Swift:

@implementation UIImage (Extra)

+ (UIImage *)validImageNamed:(NSString *)name
{
    UIImage *image = [self imageNamed:name];
    NSAssert(image, @"Unable to find image named '%@'", name);
    return image;
}

This asks to be implemented as an convenience init, but how can I check if designated initializer self.init(named:) succeeds?

extension UIImage {
    convenience init(validateAndLoad name: String!) {
        self.init(named: name)

        // need to assert here if self.init fails
    }

When self.init(named:) call fails, extension's init stops executing.

I've tried creating an UIImage instance and assigning it to self, but this doesn't compile.

Of course, a helper method can be used like in ObjC version:

extension UIImage {

    class func validImage(named name: String) -> UIImage {
        var image = UIImage(named: name)
        assert(image == nil, "Image doesn't exist")
        return image
    }

But is there a way to implement this using an initializer?

like image 857
Zmey Avatar asked Jun 13 '14 13:06

Zmey


2 Answers

You can now create failable initializers in extensions; however, the initializers can not be defined in a protocol.

class Thing {

    var text:String?

}

extension Thing  {

    convenience init?(text:String) {
        self.init()
        if text == "" {
            return nil
        } else {
            self.text = text
        }
    }
}


let that = Thing(text: "Hello")
println(that?.text) //prints Optional("Hello")

let empty = Thing(text: "")
println(empty) //prints nil
like image 67
Kelton Avatar answered Oct 08 '22 06:10

Kelton


Unlike Objective-C, Swift initializers don't return self, so checking for initialization failures is not possible. An Apple engineer suggested using a factory method with an optional return type instead:

class ImageFactory {

    class func validImage(named name: String) -> UIImage? 
    {
        var image = UIImage(named:name)
        assert(image != nil, "fail")
        return image;
    }
}

The Apple engineer indicated that they are working on building something into the language that will get around using factory class methods.

like image 27
memmons Avatar answered Oct 08 '22 04:10

memmons