Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I use NSSetUncaughtExceptionHandler in Swift

At Objective-C, I call the NSSetUncaughtExceptionHandler(&exceptionHandler) method to log exceptions. How does it called in Swift?

like image 249
billy Avatar asked Aug 22 '14 07:08

billy


3 Answers

As of Swift 2 (Xcode 7), you can pass Swift functions/closures to parameters taking a C function pointer. From the Xcode 7 release notes:

Native support for C function pointers: C functions that take function pointer arguments can be called using closures or global functions, with the restriction that the closure must not capture any of its local context.

So this compiles and works:

func exceptionHandler(exception : NSException) {
    print(exception)
    print(exception.callStackSymbols)
}

NSSetUncaughtExceptionHandler(exceptionHandler)

Or with an "inline" closure:

NSSetUncaughtExceptionHandler { exception in
    print(exception)
    print(exception.callStackSymbols)
}

This does exactly the same as the corresponding Objective-C code: it catches otherwise uncaught NSExceptions. So this will be caught:

let array = NSArray()
let elem = array.objectAtIndex(99)

NOTE:- It does not catch any Swift 2 errors (from throw) or Swift runtime errors, so this is not caught:

let arr = [1, 2, 3]
let elem = arr[4]
like image 173
Martin R Avatar answered Oct 22 '22 10:10

Martin R


Update

With Swift 2, you can pass Swift functions and closures as C function pointer. See Martin R's answer below.

Original answer

You can't, as of Xcode 6 beta 6.

Swift does support passing around function pointers, but they're treated pretty much like opaque pointers. You can't neither define a C function pointer to a Swift function nor can you call a C function pointer in Swift.

That means you call NSSetUncaughtExceptionHandler() from Swift, but the handler must be implemented in Objective-C. You need a header file like this:

volatile void exceptionHandler(NSException *exception);
extern NSUncaughtExceptionHandler *exceptionHandlerPtr;

and in the implementation, you need something like this:

volatile void exceptionHandler(NSException *exception) {
    // Do stuff
}
NSUncaughtExceptionHandler *exceptionHandlerPtr = &exceptionHandler;

After you imported the header file in the Swift bridging header, you can set up the exception handler as usual:

NSSetUncaughtExceptionHandler(exceptionHandlerPtr)
like image 43
jou Avatar answered Oct 22 '22 10:10

jou


Swift 5:

1. Add this method to AppDelegate:

    func storeStackTrace() {
        NSSetUncaughtExceptionHandler { exception in
            print(exception)
            // do stuff with the exception
        }
     }

2. Call this method from didFinishLaunchingWithOptions and raise the exception immediately after

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            storeStackTrace()

            let exception = NSException(name: NSExceptionName(rawValue: "arbitrary"), reason: "arbitrary reason", userInfo: nil)
            exception.raise()


    }

3. Follow up on the console output, it will immediately print out the exception you just raised starting with the reason you provided.

like image 6
Milos Dimic Avatar answered Oct 22 '22 08:10

Milos Dimic