8

Hello I am planning to develop a simple iPhone game. I would like to be able to distinguish between a genuine crash, and the user killing the app ( by double tapping home screen and long-clicking it to kill it) .

Can someone please shed some light on what exactly happens when the user kill the app through the multitasking bar.

sergio
  • 68,819
  • 11
  • 102
  • 123
Gapton
  • 2,044
  • 2
  • 20
  • 33

3 Answers3

15

If your app is in the background and suspended when the user kills it, it will receive no notification. This accounts for the majority of cases.

If your app is currently running in the background (there are only very specific categories of apps that can do that), then it receives applicationWillTerminate.

Indeed, Apple is very clear as to the fact that you should save any relevant data before entering the background. Have a look at this (chapter "Responding to Application Termination"):

Even if you develop your application using iOS SDK 4 and later, you must still be prepared for your application to be killed without any notification. The user can kill applications explicitly using the multitasking UI. In addition, if memory becomes constrained, the system might remove applications from memory to make more room. If your application is currently suspended, the system kills your application and removes it from memory without any notice. However, if your application is currently running in the background state (in other words, not suspended), the system calls the applicationWillTerminate: method of your application delegate. Your application cannot request additional background execution time from this method.

EDIT:

about the "saying sorry" thing...

you can certainly do that on the next launch. simply store a key in NSUserDefaults and remove it when the app enters the background (I hope all this sounds familiar to you, otherwise look into the UIApplicationDelegate protocol).

when the app starts up, you check the key; if it is there, then the app was not closed by the user; if the app is not there, then the user at least moved the app to the background and did not experience any sudden termination...

sergio
  • 68,819
  • 11
  • 102
  • 123
  • Thanks for the reply. So I just have to accept that killing app, most of the time, is non-detectable. That brings me back to my first question: Is there a way to detect a genuine crash? – Gapton Aug 11 '11 at 07:50
  • Say, I want to take the chance to say sorry to the users if my app crashes. It wouldn't make sense to say sorry if the user kill the app himself (non-detectable anyway) , is it possible to do this when the app has genuinely crashed? Or is this entirely non-detectable as well? The "sorry message" can be displayed at the time of crashing, or at NEXT application launch, it doesn't matter. But I really want to be able to detect it, and distinguish it from app-kill. Thanks in advance – Gapton Aug 11 '11 at 07:54
  • If my app costs money per round of game, I would like to refund the player if the game crash (my fault). Having a refund mechanism is good for customer satisfaction but can be a huge security risk. I am new to iPhone Dev, but are there many games that are paid by each round, that crashes, and simply does nothing to refund the angry player? – Gapton Aug 11 '11 at 08:07
  • sorry, I have no idea about that... I think that the solution I proposed should work for your case, the user cannot easily tramp it; whether it is a good business opportunity, I have no idea... – sergio Aug 11 '11 at 08:13
  • Thanks. I never thought of storing a flag in NSUserDefaults! Seeing that in order to kill the app, the user MUST click the Home button, I guess ApplicationDidEnterBackground can be used to set a flag that the user closed it. ApplicationDidFinishLaunching can check the flag, if it was NOT closed by the user, then there must have been a crash. ApplicationWillEnterForeground will NOT check for this since if the app crashed the app cannot "resume" anyway. Thanks alot!!! – Gapton Aug 11 '11 at 08:16
7

For iOS6 and later there is a way to do this. A side effect of State Restoration is that it will delete the state when there is either a crash during restore or a user manually kills the app. You can use this to your advantage to detect a user manually killing the app.

From the documentation:

Be aware that the system automatically deletes an app’s preserved state when the user force quits the app. Deleting the preserved state information when the app is killed is a safety precaution. (The system also deletes preserved state if the app crashes at launch time as a similar safety precaution.) If you want to test your app’s ability to restore its state, you should not use the multitasking bar to kill the app during debugging. Instead, use Xcode to kill the app or kill the app programmatically by installing a temporary command or gesture to call exit on demand.

The following code assumes that you already have a BOOL for crash detection called _didCrashInLastSession. There are different approaches for getting this value such as this 3rd party library. In your code call the method [self getLaunchType] to see which type of launch you are dealing with and act on that accordingly. Put the following in your AppDelegate.m:

typedef NS_ENUM(NSInteger, LaunchType) {
    LaunchTypeUnknown,
    LaunchTypeNewInstall,
    LaunchTypeNormalLaunch,
    LaunchTypeCrashedLastSession,
    LaunchTypeUserManualQuit,
};

static BOOL hadStateToRestore = NO;
static NSString * const kAppHasEverRunKey = @"appHasEverRun";

- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder
{
    // Called when going into the background
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAppHasEverRunKey];
    [[NSUserDefaults standardUserDefaults] synchronize];

    return YES;
}

- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder
{
    // Called on start up
    hadStateToRestore = YES;
    return YES;
}

- (LaunchType)getLaunchType
{
    if (_didCrashInLastSession) {
        return LaunchTypeCrashedLastSession;
    }

    if (![[NSUserDefaults standardUserDefaults] boolForKey:kAppHasEverRunKey]) {
        return LaunchTypeNewInstall;
    }

    if (!hadStateToRestore) {
        return LaunchTypeUserManualQuit;
    }

    return LaunchTypeNormalLaunch;
}

Update: At least one 3rd party SDK breaks this technique: Urban Airship.

Skotch
  • 3,072
  • 2
  • 23
  • 43
2

You can do it through your device also. Connect your device to your machine. Run xcode and go to organizer. There select your device and device logs. There you can also see crash logs of your app or game.

manishnath
  • 444
  • 1
  • 3
  • 12
  • Thanks for the input, but I want to be able to detect non-provisioned devices that I cannot physically access (my potential customers' devices) – Gapton Aug 11 '11 at 08:11
  • Ok then you can use analytics like Localytics or flurry and add an event trigger at applicationwillterminate or applicationdidenterbackground. Applicationdidenterbackground will fire when the user just closes the app and the former will be called when he kills it. You can make a free account at localytics or flurry and then add an event using their api which will record and graph all such events from customers. – manishnath Aug 11 '11 at 08:37