Search code examples
iosobjective-cswifterror-handlingcross-language

How can I throw an NSError from a Swift class and catch it in an Objective-C class?


I need to implement a try-catch structure in Objective-C to handle Swift thrown NSErrors.

I've written an NetService manager with Swift code and I am implementing it on an already existent Objective-C UI.

However, whenever I throw an error from my Swift class, the try-catch structure fails to catch the error and proceeds to the finally block.

Swift error definition:

enum NEONetServiceErrors: Int
{
    case requestMadeWithoutIp
}

struct NEONetServiceErrorStrings
{
    let requestMadeWithoutIp = ["NEONetService Error: Request made without IP": NEONetServiceErrors.requestMadeWithoutIp]
}

Swift error throwing:

@objc func requestHelloPage() throws
{
    if serviceiPAddress != nil
    {
        makeHelloRequest()
    }
    else
    {
        throw NSError(domain: errorStrings.domain, code: NEONetServiceErrors.requestMadeWithoutIp.rawValue, userInfo:errorStrings.requestMadeWithoutIp)
    }
}

Objective-C properties:

@property NEONetServiceManager* netServiceManager;
@property NSError* _Nullable __autoreleasing * _Nullable netServiceError;

Objective-C error handling:

- (IBAction)pressUpdateButton:(id)sender
{
    @try
    {
        [self.netServiceManager requestHelloPageAndReturnError: self.netServiceError];
    }
    @catch (NSException *exception)
    {
        NSLog(@"Throwing");
    }
    @finally
    {
        NSLog(@"Finally");
    }
}

Output:

2019-10-18 14:47:03.289268-0300 NEOFirmUpdate[16533:2389800] Start
2019-10-18 14:47:03.292696-0300 NEOFirmUpdate[16533:2389800] Finally

Could you help me figure out what I am doing wrong with my error-handling?


Solution

  • The problem is that a Swift Error / Objective-C NSError is not an NSException. You are configured to catch NSExceptions but that is irrelevant.

    The way to "catch" an NSError in Objective-C when Swift throws an Error is by indirection with the NSError** parameter, just as it always has been.

    NSError* err = nil;
    BOOL ok = [self.netServiceManager requestHelloPageAndReturnError:&err];
    if (ok) {
        // proceed normally
    } else {
        // you got an error, it is sitting in `err`
    }
    

    (Notice how Swift supplies a BOOL result exactly so you can implement the correct pattern.)