Friday, 4 January 2013

Programmatically terminate an iOS application (with animation)

Please view this page in desktop version, or visit it from a PC. It contains syntax-highlighted code snippets which are badly displayed on mobile devices.

Before reading, please note that proper AppStore apps should not programmatically terminate themselves, as it's against Apple's recommendations. Also, I'll be using private API function calls in this code, which will automatically have your app rejected. This approach is only meant for open application development and/or Cydia submission.

iOS applications may be programmatically terminated by calling the private method:
[[UIApplication sharedApplication] terminateWithSuccess];

Or the C function:

These, however, will instantly terminate the app, without showing any animation. Basically, it will look like the app has crashed.

The only method that will show an animation while sending the app in background is the private:
[[UIApplication sharedApplication] suspend];

This method will act differently depending on the device and iOS version:
  • On iOS 4.0 and later, and if the device supports multitasking, it will suspend the application and send it to background.
  • On previous iOS versions, or if the device does not support multitasking, it will actually close the application.

To have the app terminated even in the first case, you might want to add the "UIApplicationExitsOnSuspend" key to the app's Info.plist. This works fine if you wish to terminate the app every time you call the "suspend" method, or the user presses the home button.

However, you might want to selectively suspend or close the app depending on various circumstances, but still retain the zooming animation (which is lost by calling "exit" or "terminateWithSuccess"). I came up with this approach: enable background execution, call the suspend method, then kill the app after a certain delay.

The most coherent solution would be using a category to extend the UIApplication class with a "close" method. Here's the code I have been using in my apps:

// Needed to directly call the private methods
@interface UIApplication (existing)
- (void)suspend;
- (void)terminateWithSuccess;

@interface UIApplication (close)
- (void)close;

@implementation UIApplication (close)

- (void)close
    // Checks if the current device supports background execution
    BOOL multitaskingSupported = NO;
    // iOS < 4.0 compatibility check
    if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)])
        multitaskingSupported = [UIDevice currentDevice].multitaskingSupported;
    // Checks if application responds to selector "suspend" (good practice, we're using a private method)
    if ([self respondsToSelector:@selector(suspend)])
        if (multitaskingSupported)
            [self beginBackgroundTaskWithExpirationHandler:^{}];
            // Change the delay to your liking. I think 0.4 seconds feels just right (the "close" animation lasts 0.3 seconds).
            [self performSelector:@selector(exit) withObject:nil afterDelay:0.4];
        [self suspend];
        [self exit];

- (void)exit
    // Again, good practice
    if ([self respondsToSelector:@selector(terminateWithSuccess)])
        [self terminateWithSuccess];

This will allow you to call:
[[UIApplication sharedApplication] close];
And have your application gracefully animate and close itself.

No comments:

Post a Comment

Please only use comments to give feedback or suggestions. Support requests are only accepted via email.