Swift 2 error handling cannot express optional results when bridging from Objective-C

I depend on a legacy C++ library for which I need to build a bridge into Swift. The API is similar to a standard XML DOM and a database-like queries at the same time. Querying for a property of an object can return a value (e.g. Int), absence of a value (nil) or an error (something went wrong). Ideally I would like to have a Swift call like this:


func getInt() throws -> NSNumber?


It seems, it is not possible with the current Swift 2 compiler since the Objective-C method


-(NSNumber*)getIntWithError:(NSError**)error


will translate into


func getInt() throws -> NSNUmber


It doesn't seem to be possible to express that the result is actually optional. I have already posted a bug report, however may someone have an idea for a workaround?


Thanks a lot in advance!

Answered by dta256 in 12102022

At the moment, it seems, the only solution would be to create a custom error meaning "value is not available" and handle it in the catch block. Semantically not perfect, but works.

What if you nullability annotate your Objective-C?

That makes no difference at all.


-(nullable NSNumber*)getIntWithError:(NSError**)error


still creates


func getInt() throws -> NSNumber


In fact I tried a backwards conversion, i.e. created a Swift class


@objc class TestErrors : NSObject
{
    override init()
    {
    }
    var prop : Int = 0
    func getInt() throws ->NSNumber?
    {
        if prop < 0
        {
            throw NSError(domain: NSURLErrorDomain, code: NSUserCancelledError, userInfo: nil)
        }
        else if prop > 0
        {
            return NSNumber(integer:prop)
        }
        else
        {
            return nil
        }
    }
}

and tried to bridge it to Objective-C. However in this case the getInt() method is not bridged:


@interface TestErrors : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@property (nonatomic) NSInteger prop;
@end


Only if I change the signature to


func getInt() throws ->NSNumber


I obtain this:


@interface TestErrors : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@property (nonatomic) NSInteger prop;
- (NSNumber * __nullable)getIntAndReturnError:(NSError * __nullable * __null_unspecified)error;
@end


I have also tried what happens if I just return nil in my Objective-C code. In that case I get a run-time error (somehow expected).

Accepted Answer

At the moment, it seems, the only solution would be to create a custom error meaning "value is not available" and handle it in the catch block. Semantically not perfect, but works.

I think this is the main goal with swift 2 and errors, you call a function and if it's unable to return something, it'll throw an error.

So writing something like this

func getInt() throws -> NSNumber?

would be ambiguous since it'll either throw an error or return a value.

Swift 2 error handling cannot express optional results when bridging from Objective-C
 
 
Q