Swift not recognizing Obj-C subclass of NSError?

In an Objective-C library I have a subclass of NSError that provides some library-specific convenience initializers:


@interface MyError : NSError { / … */ }


This error type is used throughout the library's public interface. Since this is an error type I would expect Swift to recognize it when using the library, e.g. e.g. `-(BOOL)doSomethingOrError:(MyError**)err` would be treated as `doSomething() throws`, but it is not.


Is there a way to make this work? Can I somehow force the "throws" interpretation in a bridging header, or somehow annotate my Objective-C library so that Swift fully recognizes the error type? I tried declaring `class MyError : Error {}` but Swift complains that it already knew that.

Replies

No, I think it can't work because you are promising that only MyError* instances can be returned, not NSError* generally. That means a general "throw" statement — in a method called by your code, so not necessarily one you wrote — could not throw an arbitrary Error-conforming object, since Swift doesn't know how to turn that into a MyError*.


This only works in your pure Obj-C code because non-MyError* instances of NSError* can never cross a (MyError**)-parameter boundary. This concept doesn't translate into Swift's error handling.


Wouldn't the correct thing to do is for your code to use the standard (NSError**) parameter type everywhere. Your methods producing errors would still return an actual MyError*. Methods handling errors might need to switch on the actual type, if MyError has additional API other than initializers, otherwise error handling wouldn't care.

Thanks! Converting the signatures to NSError may be a viable path forward — afaict the intention of this subclass was mostly to help in the creation of errors rather than their consumption.


I still don't quite understand why (from the perspective of a caller of the code) that Swift can't treat these as methods that throw, even if they are only ever (at least ostensibly) throwing a particular subclass of error?

You might get a more definitive answer as to why this happens, you could try asking the question over at forums.swift.org. Since this is a compile-time issue (the Swift compiler has to convert a .h file declaration into Swift form), it may well be that the conversion process is basically lexical, so superclass relationships are not re-constructed there. Or, since the "(NSError**) outError" pattern is only a convention, it may have been thought safer not to make assumptions about variations on the pattern.


As to the logic of your argument:


>> they are only ever (at least ostensibly) throwing a particular subclass of error


if that's true (and it sounds reasonable to me) then it's also an argument why you could write the Obj-C parameter types as NSError** instead. Conversely, if there's any reason why you could not use NSError** instead, that would be a reason why it wouldn't work in Swift.


There are also other approaches you could take, if this is just about initializers:

— add the initializers to NSError itself using a category (using prefixed names to avoid clashes with private method names)


— convert the initializers to static (factory) methods on a utility class


(The second one is the approach I've used in the past.) Either way, you would eliminate the MyError class completely.