Checking version and device when restoring state

To finish up this recent series of posts on state preservation and restoration I wanted to deal with the situation where you want to prevent previously saved state data from being restored. By way of a recap remember that to enable state preservation and restoration the application delegate must return YES for the following methods:

- (BOOL)application:(UIApplication *)application shouldSaveApplicationState:(NSCoder *)coder {
  return YES;
}

- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
  return YES;
}

It is the second of these methods, application:shouldRestoreApplicationState:, that is going to be useful when we want to prevent state restoration. The NSCoder argument contains the restoration data that UIKit previously saved for the app. This coder data always contains two special keys added by UIKit:

  • UIApplicationStateRestorationBundleVersionKey
  • UIApplicationStateRestorationUserInterfaceIdiomKey

The first key can be used to retrieve an NSString which is the bundle version of the app that saved state. The second key can be used to retrieve an NSNumber which contains the user interface idiom (iPhone or iPad) for the device used to save state.

Bundle Version

One situation where you may want to prevent state restoration from happening is when you release an update that makes significant changes to the view hierarchy. Attempting to restore the user interface in this scenario is unlikely to be useful.

For the purposes of this example I am using an incrementing integer value for the bundle version. See this earlier post on setting iPhone application build versions for the difference between the bundle version and the release/marketing version. So assuming that I update the bundle version from “1” to “2” and want to be sure that I never restore state for bundle version “1”:

Bundle Version

The modified implementation of the application:shouldRestoreApplicationState: method would be as follows:

#define BUNDLEMINVERSION 2

- (BOOL)application:(UIApplication *)application shouldRestoreApplicationState:(NSCoder *)coder {
  NSString *restorationBundleVersion = [coder decodeObjectForKey:UIApplicationStateRestorationBundleVersionKey];
  if ([restorationBundleVersion integerValue] < BUNDLEMINVERSION) {
    NSLog(@"Ignoring restoration data for bundle version: %@",restorationBundleVersion);
    return NO;
  }
  return YES;
}

User Interface Idiom

A second scenario where we would most likely not want to restore state is if the current device type (iPhone or iPad) does not match the device type that saved the state data. The only way that I can think to create that situation is to restore an iPhone backup to an iPad device (or vice versa). It would also need to be a universal app which is not the case in this example but we will ignore that for the purposes of this discussion.

The additional code to retrieve and check the user interface idiom in the application:shouldRestoreApplicationState: method is shown below:

UIDevice *currentDevice = [UIDevice currentDevice];
UIUserInterfaceIdiom restorationInterfaceIdiom = [[coder decodeObjectForKey:UIApplicationStateRestorationUserInterfaceIdiomKey] integerValue];
UIUserInterfaceIdiom currentInterfaceIdiom = currentDevice.userInterfaceIdiom;
if (restorationInterfaceIdiom != currentInterfaceIdiom) {
  NSLog(@"Ignoring restoration data for interface idiom: %d",restorationInterfaceIdiom);
  return NO;
}

Wrapping Up

You can find the updated version of the Restorer Xcode project in my GitHub CodeExamples repository.