0

I want to display glass shatter animation before app closes. I manage to capture screenshot of the screen before app closes by setting ExceptionHandler

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler); 
    return YES;
}

void uncaughtExceptionHandler(NSException *exception) {

    UIWindow *lastWindow = [[UIApplication sharedApplication].windows lastObject];
    UIGraphicsBeginImageContext(lastWindow.bounds.size);
    [lastWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *pngImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    NSData * data = UIImagePNGRepresentation(pngImage);
}

But then I cannot even add a simple image to the view, app crashes before next drawing cycle. Is it possible to display anything on the screen before app shuts down?

tadasz
  • 4,366
  • 5
  • 26
  • 33
  • duplicate question, next time use search before you are posting a question: http://stackoverflow.com/questions/1787254/showing-an-alert-in-an-iphone-top-level-exception-handler – art-divin May 03 '13 at 13:07

2 Answers2

3

Once you've fired an exception, the state of your application is undefined; there's no telling what your application was in the middle of doing, and while there are a number of things that can go wrong (see the link above), the one I'll focus on here is data corruption.

Some crash reporters attempt to submit the crash report over the network immediately upon program termination; in your case, you're attempting to keep the app running and display a message, which has the same effect: keeping your application running means execution of the application's own code, and the application is then free to attempt to write potentially corrupt user data.

Consider a Core Data-based application, in which a model object is updated, and then saved:

person.name = name;
person.age = age; // an exception occurs here
person.birthday = birthday;
[context save: NULL];

At the time of the crash, the managed object context contains a partially updated record — certainly not something you want saved to the database. However, if your uncaught exception handler then proceeds to keep your application executing, any network connections, timers, or other pending runloop dispatches in your application will also be run. If any of your application code dispatched from the runloop contains a call to -[NSManagedObjectContext save:], you'll write a partially updated record to the database, corrupting the user's data.

The safest thing to do when your application is in an unknown/non-determinate state is to simply exit.

landonf
  • 641
  • 1
  • 5
  • 9
  • The problem is not to do something when app crashes, I can perform functions like saving photo to memory and etc. But I want to make crash beautiful - instead of just shutting down I want to display animations something like Screen Glass Shatter ;) – tadasz May 03 '13 at 23:47
1

Well, dunno if this will help you but in my apps I've managed to show an UIAlertView with an explanation to the user about the crash, the exception type, its description and the stack trace (all using the NSSetUncaughtExceptionHandler method), like this:

Sample in spanish

Then I offer the recommended option of killing the app or continue despite that the app may be unstable. In my case it affected partially the app functionality, so in most of the cases the user could save its work and close safely the app.

If you want I can edit the answer and post here the code (I'll have to search through my Xcode projects folder, that's why I haven't posted it).

EDIT:

On the AppDelegate delegate's method willFinishLaunchingWithOptions I set the NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

Then on I create the handler method as follows:

static void uncaughtExceptionHandler(NSException *exception)
{
    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"kDisculpe", nil) message:[NSString stringWithFormat:@"%@ %@%@ %@%@ %@", NSLocalizedString(@"kErrorText", nil), [exception name], NSLocalizedString(@"kErrorDescripcion", nil), [exception reason], NSLocalizedString(@"kErrorTrazaPila", nil), [exception callStackReturnAddresses]] delegate:[[UIApplication sharedApplication] delegate] cancelButtonTitle:NSLocalizedString(@"kContinuar", nil) otherButtonTitles:NSLocalizedString(@"kSalir", nil), nil] show];
    [[NSRunLoop currentRunLoop] run];
}

Then on the AlertView's delegate method clickedButtonAtIndex I set:

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if ([[alertView title] isEqualToString:NSLocalizedString(@"kDisculpe", nil)]) {
        switch (buttonIndex) {
            case 0:
                if ([[alertView title] isEqualToString:NSLocalizedString(@"kDisculpe", nil)]) {
                    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"kAdvertencia", nil) message:NSLocalizedString(@"kAppContinuaraInestable", nil) delegate:[[UIApplication sharedApplication] delegate] cancelButtonTitle:NSLocalizedString(@"kContinuar", nil) otherButtonTitles:nil] show];
                }
                break;
            case 1:
                exit(0);
                break;
        }
    }
}

Notice that the only important thing I did is the [[NSRunLoop currentRunLoop] run]; I hope this will help you.

CSolanaM
  • 3,138
  • 1
  • 20
  • 21