Home > Blockchain >  Call objc code that throws exception in Swift
Call objc code that throws exception in Swift

Time:06-22

I have this code in ObjC/C:

AVCaptureFocusMode GetFocusModeWithString(NSString *mode) {
  if ([mode isEqualToString:@"locked"]) {
    return AVCaptureFocusModeLocked; 
  } else if ([mode isEqualToString:@"auto"]) {
    return AVCaptureFocusModeAutoFocus;
  } else {
    @throw [NSError errorWithDomain: @"FocusError", code: 12];
  }
}

It used to be working fine when calling from ObjC code. Now I am rewriting the caller side using swift. However, in swift, this code does not actually throw:

// does not work, swift thinks this function does not throw. 
do {
  try GetFocusModeWithString(@"auto")
}

Am I doing anything wrong here? Is the ObjC code bad? how can I improve the ObjC code to work nicely with swift?

CodePudding user response:

Objective C does not have do/try/catch semantics the way that Swift does.

@throw causes a runtime exception. This isn't something you can catch in Swift.

The way that you can integrate Cocoa error handling with Swift is described in the Swift Objective C interoperability documentation and this answer

First declare your Objective-C function to accept a pointer to an instance of NSError. Then you can flag that parameter with function with __attribute__((swift_error(nonnull_error))).

This will cause a Swift exception if the error is non-null when the function returns

- (AVCaptureFocusMode) getFocusModeWithString:(NSString *) mode error: (NSError **)error __attribute__((swift_error(nonnull_error))); {
    *error = nil;
    if ([mode isEqualToString:@"locked"]) {
        return AVCaptureFocusModeLocked;
    } else if ([mode isEqualToString:@"auto"]) {
        return AVCaptureFocusModeAutoFocus;
    }
    *error = [NSError errorWithDomain:@"FocusError" code:12 userInfo:nil];
    return -1;
}

Now, in your Swift code you can call it with

do {
  let focusMode = try getFocusMode(with: "auto")
} catch {
  print(error)
}

Note that this will change your method signature in Objective-C; You will need to pass &error to the method and check its value on return.

CodePudding user response:

Objective-C exceptions are not compatible with Swift. Here's what Apple says:

Handle Exceptions in Objective-C Only

In Objective-C, exceptions are distinct from errors. Objective-C exception handling uses the @try, @catch, and @throw syntax to indicate unrecoverable programmer errors. This is distinct from the Cocoa pattern—described above—that uses a trailing NSError parameter to indicate recoverable errors that you plan for during development.

In Swift, you can recover from errors passed using Cocoa’s error pattern, as described above in Catch Errors. However, there’s no safe way to recover from Objective-C exceptions in Swift. To handle Objective-C exceptions, write Objective-C code that catches exceptions before they reach any Swift code.

  • Related