Search code examples
iosobjective-cswiftinitializationobjective-c-swift-bridge

How to bridge Objective-C initWithError: method into Swift


I have a class defined in Objective-C, whose initializer is -initWithError: (the initializer can fail due to a dependence on an outside resource). I want this to bridge into Swift as init() throws. The regular initializer inherited from NSObject, -init, can be marked unavailable, as I don't want it to be used.

In Objective-C, I have:

@interface Foo : NSObject

- (instancetype _Nullable)initWithError:(NSError **)error;

@end

This works fine in Objective-C, of course.

In Swift, -initWithError gets bridged as init(error: ()) throws. This is presumably because removing withError: from the method name results in init() which conflicts with the inherited plain -init initializer. This can be called from Swift like so:

let foo = try Foo(error: ())

This is strange looking, as the error parameter is void. It would certainly be better if this were imported as init() throws. The obvious solution is to mark -init using NS_UNAVAILABLE in the Objective-C header. Unfortunately, this doesn't work. -initWithError: still gets bridged as init(error: ()), and trying to call try Foo() results in a compiler error saying that init() is unavailable in Swift.

Is there a more elegant solution to this so that try init() just works?


Solution

  • You can rename the function using NS_SWIFT_NAME. In this case:

    - (instancetype _Nullable)initWithError:(NSError **)error NS_SWIFT_NAME(init());
    

    That said, this feels like a compiler bug. I'd suggest opening a defect.