Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Cannot override 'init' which has been marked unavailable" prevents overriding empty init

Tags:

ios

swift

swift2

I have a situation where I'm trying to override NSError to provide me an instance of an error I'm going to be re-using a lot.

My code was working until I updated Xcode and converted to Swift 2.

public class NAUnexpectedResponseTypeError: NSError {
    public convenience init() {
        let messasge = "The object fetched by AFNetworking was not of an expected type."
        self.init(
            domain: "MyDomain",
            code: 6782,
            userInfo: [NSLocalizedDescriptionKey: messasge]
        )
    }
}

The compiler says Cannot override 'init' which has been marked unavailable. I was able to hack around it by doing this:

public class NAUnexpectedResponseTypeError: NSError {
    public class func error() -> NSError {
        let message = "The object fetched by AFNetworking was not of an expected type."
        return NAUnexpectedResponseTypeError(
            domain: "MyDomain",
            code: 6782,
            userInfo: [NSLocalizedDescriptionKey: message]
        )
    }
}

So, my question is:

  1. Is there a way to add an empty init method in a situation like this?
  2. If yes to 1, is that a bad idea for some reason?
  3. Is my workaround with the class method an appropriate way to mitigate this problem?

EDIT:

I came up with another workaround that I like better than the workaround with the class method. I'm still not happy that I can't override the empty init method though.

public class NAUnexpectedResponseTypeError: NSError {
    public convenience init(message: String?) {
        var errorMessage: String
        if let message = message {
            errorMessage = message
        } else {
            errorMessage = "The object fetched by AFNetworking was not of an expected type."
        }
        self.init(
            domain: "MyDomain",
            code: 6782,
            userInfo: [NSLocalizedDescriptionKey: errorMessage]
        )
    }
}
like image 339
Jonathan Avatar asked Sep 23 '15 20:09

Jonathan


2 Answers

Since NSError is immutable, there's no reason to create multiple instances of the same data. Just create a single, constant, instance:

let NAUnexpectedResponseTypeError = NSError(domain: "MyDomain",
    code: 6782,
    userInfo: [NSLocalizedDescriptionKey: "The object fetched by AFNetworking was not of an expected type."]
)

If you have a situation that isn't constant, then it is almost always better to extend rather than subclass NSError. For example:

extension NSError {
    class func MyError(code code:code, message: String) -> NSError {
        return NSError(domain: "MyDomain", 
                       code: code,
                       userInfo: [NSLocalizedDescriptionKey: message])
   }
}

This kind of extension (as a category) has a long history in ObjC, and is a good pattern to bring to Swift (if you can't easily use enum ErrorTypes, which are even better Swift).

In many cases I find it even easier just to have a top-level function for this, rather than extending NSError. For example:

private func makeError(code code:code, message: String) -> NSError {
    return NSError(domain: "MyDomain", 
                   code: code,
                   userInfo: [NSLocalizedDescriptionKey: message])
}

(Personally I use these kinds of functions all the time in Swift when I have to use NSError. In ObjC, I typically used categories on NSError. Not sure why I changed, but it feels more natural.)

like image 91
Rob Napier Avatar answered Nov 28 '22 19:11

Rob Napier


You can't override the empty init, it's marked as unavailable so you're not supposed to be able to do anything with it.

You do have another workaround though

public class NAUnexpectedResponseTypeError: NSError {

    public convenience init(message: String = "The object fetched by AFNetworking was not of an expected type.") {
            self.init(
                domain: "MyDomain",
                code: 6782,
                userInfo: [NSLocalizedDescriptionKey: message]
        )
    }
}

I didn't test it, but it should work.

Since you're using swift 2.0, why don't you make your instance conform to error type instead of subclassing NSError ? It would be cleaner and more idiomatic.

like image 24
Gulypy Avatar answered Nov 28 '22 19:11

Gulypy