17

In Objective-C, the NSSetUncaughtExceptionHandler can register some functionality to do some last minute logging about the exception.

This doesn't catch something crashing from Swift.

Is it possible to do something like this at a global level in Swift? E.g. do some logging if a crash happens in Swift code, like forced unwrapping a nil optional.

Specifically, I'm doing a utility to log network traffic in the app, and I would like to flush my in-memory data to disk if a crash happens.

Ken Ko
  • 1,439
  • 2
  • 14
  • 20

4 Answers4

10

NSSetUncaughtExceptionHandler works only with NSExceptions. See this SO answer for brilliant explanation.

To catch Swift run time errors implement signal handler for SIGTRAP. As far as I know, Swift code will terminate the program with SIGTRAP exception type if it detects an unexpected condition at runtime i.e only SIGTRAP is helpful to catch Swift errors, rest like SIGSEGV, SIGBUS, SIGILL do not work. I found this info in this apple link.

If your code is a mix both Objective-C and Swift, then implement both NSSetUncaughtExceptionHandler and signal handler to handle crashes.

For understanding and implementing Signal handling refer this link.

Hope this helps.

Community
  • 1
  • 1
SHN
  • 735
  • 6
  • 23
  • 3
    I added a signal handler for most signals (`SIGTRAP` included), however it does not handle "fatal error: unexpectedly found nil while unwrapping an Optional value". I also added an exception handler with `NSSetUncaughtExceptionHandler()`... – Nicolas Miari Nov 16 '16 at 11:31
  • @NicolasMiari facing the same in issue any workaround you found ? – Jack May 16 '18 at 06:32
  • 1
    @Jack See my question/answer here: https://stackoverflow.com/a/40712296/433373. This is as far as I ever got regarding this, and I have moved away from that project since. I hope it helps. – Nicolas Miari May 16 '18 at 06:35
  • I see `SIGILL` for nil dereferences. – Rhythmic Fistman Jul 24 '19 at 05:32
6

Swift 5 you can handle app crash like this

NSSetUncaughtExceptionHandler { (exception) in
   let stack = exception.callStackReturnAddresses
   print("Stack trace: \(stack)")

    }
Zain Anjum
  • 416
  • 4
  • 12
4
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);

This will works for most situations. But I'm also trying to find some way to catch all crashes. Well, if you comd+click to those signals to see documents, you will find there are more than 20 kinds of signals, what I've done is signal(,) all most all kinds of signals. That seems works, but maybe still some crash cannot collect.

sig
  • 5,896
  • 2
  • 24
  • 31
Benjamin
  • 99
  • 6
1

There are multiple aspects to your questions, let me try to answer them:

  1. NSSetUncaughtExceptionHandler only catches uncaught exceptions which is only a small subset of possible crashes.
  2. Exceptions in Objective-C are defined to be fatal and it is not recommended to catch them and even more not recommended to run any non-async safe code, which includes any Objective-C and Swift code
  3. To catch crashes you need to setup signal handlers and then only run async-safe code at the time of the crash, which is only a small subset of C. Especially you may not allocate new memory at crash time at all.
  4. Please remember, when your app crashed it is in a highly unstable code and object or variable references may point to something completely unexpected. So you really shouldn't do anything at crash time at all.
  5. The best open source way to work with crashes is using PLCrashReporter, which also provides method to invoke code at crash time. But again, this has to be async-safe.

To your specific scenario: you should not do that and not try to. This may cause user data to get corrupted or more data to be lost. The app crashed hard with serious issues, rather fix the issue.

Kerni
  • 15,071
  • 5
  • 33
  • 58