9

I'm using the following code to capture exceptions in my app:

void uncaughtExceptionHandler(NSException *exception) {
    [FlurryAPI logError:@"Uncaught" message:@"Crash!" exception:exception];
}

Just wondering whether I can pin-point, line numbers, UIView, classes, etc that the error's occurring on. Ideally I'd like as much detailed information as I can get, since it's captured by FlurryAPI analytics.

FlurryAPI: http://www.flurry.com/

gotnull
  • 26,454
  • 22
  • 137
  • 203

2 Answers2

16

I ended up going with this:

void uncaughtExceptionHandler(NSException *exception) {
    NSArray *backtrace = [exception callStackSymbols];
    NSString *platform = [[UIDevice currentDevice] platform];
    NSString *version = [[UIDevice currentDevice] systemVersion];
    NSString *message = [NSString stringWithFormat:@"Device: %@. OS: %@. Backtrace:\n%@",
                         platform,
                         version,
                         backtrace];

    [FlurryAPI logError:@"Uncaught" message:message exception:exception];
}

UPDATE (based on @TommyG's comment below):

Add NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler); to the end of your - -(BOOL)application:didFinishLaunchingWithOptions: method in AppDelegate. Then add the above method to the AppDelegate as well.

gotnull
  • 26,454
  • 22
  • 137
  • 203
  • 1
    where do you place this method? is it in the application delegate? I get a warning saying that there is no previous prototype for this method.. – TommyG Mar 19 '12 at 21:12
  • 1
    @TommyG Add `NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);` to the end of your `- (BOOL)application:didFinishLaunchingWithOptions:` method in AppDelegate. Then add the above method to the AppDelegate as well. – alex Jul 30 '12 at 07:32
  • 1
    Does this actually give you a stack trace? – jjxtra Oct 06 '12 at 01:38
3

You can make advantage of the precompiler and write a macro which gathers all the values, eg:

#define __ThrowException(name, reason, class, function, file, line, info) [NSException exceptionWithName:name reason:[NSString stringWithFormat:@"%s:%i (%@:%s) %@", file, line, class, function, reason]  userInfo:info];
#define ThrowException(name, reason, info) __ThrowException(name, reason, [self class], _cmd, __FILE__, __LINE__, info)

However, this only works when you throw an exception and from inside an ObjC function (self and _cmd are the very first parameters you get in an ObjC function, where self is an id which points to the class and _cmd to the selector which can be (currently!) typecasted to const char).

However, if you want this only for Foundation exceptions, you have two options:

  1. Wrap everything that might throw an exception in @try() @catch() blocks and then throw a new, custom, exception
  2. Get the stack trace, this is what might be a little more hard as your app is possible in an inconsistent state and can't gather all the values. Gathering the current stack trace is covered here in great detail.
JustSid
  • 25,168
  • 7
  • 79
  • 97
  • I see, would I be defining those macros within the AppDelegate? If so, how would I then go about using them within my void uncaughtExceptionHandler(NSException *exception)? – gotnull Apr 02 '11 at 04:00
  • 1
    @Fulvio Cusumano: Ah well, the macro isn't meant to be used in your handler, as it is already to late at this time to get the needed values from the precompiler, but should be used to create exceptions and then throw them. Like I said, if you only need Foundations exceptions, you can use @try() and @catch() to create a new exceptions or walk down the stack. – JustSid Apr 02 '11 at 07:56