skip to Main Content

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?

2

Answers


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

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search