1

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?

theCanabrava
  • 21
  • 2
  • 6

3 Answers3

2

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.)

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • My comment to the other answer applies here as well: Apple documents that you should check the *return value* for success or failure, before attempting to do anything with the NSError object. – Martin R Oct 18 '19 at 18:11
  • Yes @MartinR I'm just about to add that. The OP has no return value which is another problem. – matt Oct 18 '19 at 18:11
  • That is automatically translated: `@objc func requestHelloPage() throws` is imported to Obj-C as `- (BOOL)requestHelloPageAndReturnError:(NSError * *)error;` – Martin R Oct 18 '19 at 18:13
0

That's because you're using objective-c exceptions there, and not actually checking for an error. To check for errors in objective-c, you pass a reference to your pointer and your function will fill that error out if there was an issue.

NSError *serviceError = nil;
[self.netServiceManager requestHelloPageAndReturnError:&serviceError];
if (serviceError) {
    // there was a problem
}

If this is an asynchronous call, you'll need to do this in a closure instead:

NSError *serviceError = nil;
[self.netServiceManager requestHelloPage:^(NSError *error) {
    if (error) {
        // there was a problem
    }
}];
Procrastin8
  • 4,193
  • 12
  • 25
  • 2
    This is not entirely correct. You *always* check the return value first. If that is `NO` (indicating a failure) *then* you can access `serviceError`. – Martin R Oct 18 '19 at 18:06
  • 1
    Reference: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/CreateCustomizeNSError/CreateCustomizeNSError.html: Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object. – Martin R Oct 18 '19 at 18:09
  • In what cases have you seen an error filled out but a successful operation? – Procrastin8 Oct 18 '19 at 18:09
  • I haven't “seen” it because I always use the documented error checking pattern :) – Martin R Oct 18 '19 at 18:15
0

In your Objective-C code you are Catching and NSException, not an NSError

Swift automatically bridges between the Error type and the NSError class. Objective-C methods that produce errors are imported as Swift methods that throw, and Swift methods that throw are imported as Objective-C methods that produce errors, according to Objective-C error conventions.

for more information you can click here

Pedro Trujillo
  • 1,559
  • 18
  • 19