19

I am trying to set the value of an @objc object in my Swift code using obj.setValue(value, forKey: key).

It works fine when the object has the property being set. But if it doesn't, my app crashes hard with an uncaught NSException ("class is not key value coding-compliant…").

How can I catch and absorb this exception like I can in Objective-C so as to not crash my app? I tried wrapping it in a Swift try-catch, but it complains that none of the instructions throws and does nothing.

5
  • Do you really need KVC? Objects in dictionaries are supposed to be set with setObject:forKey: or key subscripting especially in Swift. Commented Jan 31, 2016 at 21:20
  • Yes, I'm writing an XML parser and need to be able to set arbitrary properties on objects by their names. Commented Jan 31, 2016 at 21:21
  • Then rather than catch an exception check if the key exists. Commented Jan 31, 2016 at 21:22
  • That would totally work. I didn't think there was a way to do that though! Commented Jan 31, 2016 at 21:23
  • 2
    Possible duplicate of Catching NSException in Swift Commented Mar 23, 2019 at 13:22

4 Answers 4

24

see this answer:

//
//  ExceptionCatcher.h
//

#import <Foundation/Foundation.h>    

NS_INLINE NSException * _Nullable tryBlock(void(^_Nonnull tryBlock)(void)) {
    @try {
        tryBlock();
    }
    @catch (NSException *exception) {
        return exception;
    }
    return nil;
}
Sign up to request clarification or add additional context in comments.

1 Comment

That did the trick! Not pretty, but I'm glad it's at least possible!
15

Swift 5 version with utility error type:

NS_INLINE NSException * _Nullable ExecuteWithObjCExceptionHandling(void(NS_NOESCAPE^_Nonnull tryBlock)(void)) {
    @try {
        tryBlock();
    }
    @catch (NSException *exception) {
        return exception;
    }
    return nil;
}
public struct NSExceptionError: Swift.Error {

   public let exception: NSException

   public init(exception: NSException) {
      self.exception = exception
   }
}

public struct ObjC {

   public static func perform(workItem: () -> Void) throws {
      let exception = ExecuteWithObjCExceptionHandling {
         workItem()
      }
      if let exception = exception {
         throw NSExceptionError(exception: exception)
      }
   }
}

Comments

13

Not the answer I was hoping for, unfortunately:

Although Swift error handling resembles exception handling in Objective-C, it is entirely separate functionality. If an Objective-C method throws an exception during runtime, Swift triggers a runtime error. There is no way to recover from Objective-C exceptions directly in Swift. Any exception handling behavior must be implemented in Objective-C code used by Swift.

Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C (Swift 2.1).” iBooks. https://itun.es/ca/1u3-0.l

My next plan of attack is to add an Objective-C function I can call out to that will wrap the attempt in @try/@catch. This really sucks, Apple.

Comments

1

I wanted to call out the answer given in this question:

Catching NSException in Swift

as you can still use the Swift do/catch with the call in a natural fashion, the next best thing to Swift actually catching ObjC exceptions as it should.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.