HiveBrain v1.2.0
Get Started
← Back to all entries
debugMinor

Unhandled Exception handler that captures a screenshot

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
exceptioncapturesthatscreenshothandlerunhandled

Problem

So, whether you're still in the development stages or your app is already on the app store, you always hope your app isn't crashing. But if it is, you want to be sure you've got good crash reports, right? Moreover, if your app is on the appstore, it may not be sufficient to wait around for Apple to upload crash reports that iOS automatically generates and submits (for users that have allowed this).

Plus, wouldn't it be nice if your crash reports came with a screenshot (when possible) of what the user saw at the instant of the crash?

This code strives to be able to accomplish some of these things.

First, the relevant parts of class categories:

UIImage+(Screenshot).m

@implementation UIImage (Screenshot)

+ (instancetype)screenshot {
    UIWindow *window = [UIApplication sharedApplication].keyWindow;
    CGSize windowSize = [window bounds].size;

    UIGraphicsBeginImageContext(windowSize);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [window.layer renderInContext:context];

    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return screenshot;
}

@end


NSDate+(NowString).m

@implementation NSDate (NowString)

+ (NSString *)nowString {
    NSDateFormatter *df = [[NSDateFormatter alloc] init];
    [df setDateFormat:@"YYYY-MM-dd HH:mm:ss zzz"];
    return [df stringFromDate:[self date]];
}

@end


The categories exist merely for convenience, and I am interested in hearing any ways to improve them, however the main code under review is the code that actually handles unhandled exceptions and generates crash logs:

AppDelegate.m

```
#import "AppDelegate.h"
#import "CustomCategories.h"

static NSString * const kKEY_CRASH_REPORT = @"CRASH_REPORT";
static NSString * const kKEY_ExceptionName = @"UnhandledExceptionName";
static NSString * const kKEY_ExceptionReason = @"UnhandledExceptionReason";
static NSString * const kKEY_ExceptionUserInfo = @"UnhandledExceptionUserInfo";
stati

Solution

+ (instancetype)screenshot;


While it is good to use the instancetype as your return type to allow for subclass, there are two problems using it here.

First, this is a class category, not a class, and the only way for a category to be included in a subclass by default is kind of hacky. You just import the file the category is in in the subclass's header.

But the bigger problem here is this:

// stuff

UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
// stuff

return screenshot;


The method doesn't actually return a dynamically typed object. It returns a UIImage object--every time. This method will never return anything other than a UIImage, but through some hacky methods, you could get the IDE to lie and claim it will return SomeUIImageSubclass. The end result though is that the returned object will ALWAYS be a UIImage and never anything else. As such, the declared return type should be changed from instancetype to UIImage:

+ (UIImage *)screenshot;

Code Snippets

+ (instancetype)screenshot;
// stuff

UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
// stuff

return screenshot;
+ (UIImage *)screenshot;

Context

StackExchange Code Review Q#56162, answer score: 8

Revisions (0)

No revisions yet.