When you go to lunch and leave something on your desk, you expect that everything will remain in place after your return. Your user expect’s the same when he presses the “Home” button on his iPhone or receives a phone call. He expects to open application and find application in the same state, as it was left.

Application state preserving is often skipped by developers. To make user happy we must care about application state saving and restoring.

What is it

The application state is restored, when user is able to continue to work with it as after waking from background. Restoration means both Appearance and Behavior. Which generally means, that user will not only see the view he saw last before application went to background, but will be able to navigate through the same navigation stack as he left.

State preservation is ViewController based. The UI architecture of your app is based on ViewControllers and relations between them. So ViewController is the main unit of application state restoration.

You control whether you want to restore some states or not. You can mark some ViewController for restoration, or just skip it. No problem.

State pereserving is Incremental development compatible. If you decided to incorporate it into your application, you can do it step by step. No need to do some extreme changes and rewrite whole product.

Apple did a lot of effort to make it easy for developers. iOS handles the accounting and provides default, easy adoptable behaviors and does almost all hard work of looping through views, recreating navigation stack, saving state and e. t. c.

Implementation

Handle the semantics

Applications are built for users. Try to look at your application from the users point of view. Notice, which parts of the work are importand for restoring and define the restoration level. When your application is a book reader, you want to start reading exactly from the place you finished. If application has difficult navigation stack, then you probably want to restore it to prevent user to repeat navigation. Everything is up to you. To you as user.

Mark your application parts for restoring

First of all, you must inform the system, that it should handle state saving. You can do it in AppDelegate by adding the following methods. Their names are pretty verbose, so we will not stop on them. You may not want to restore state, for instance, after application version update. Then just use conditions to make them return false.

func application(application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool {
  return true
}
  
func application(application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
  return true
}

For more complex restoring scenarios AppDelegate contains three more methods

// IMPORTANT: This method is not a substitute for saving your app’s data structures persistently to disk!
func application(application: UIApplication, willEncodeRestorableStateWithCoder coder: NSCoder) {

}

The state preservation system calls this method at the beginning of the preservation process. This is your opportunity to add any app-level information to state information. For example, you might use this method to write version information or the high-level configuration of your app.

func application(application: UIApplication, didDecodeRestorableStateWithCoder coder: NSCoder) {

}

The state restoration system calls this method as the final step in the state restoration process. By the time this method is called, all other restorable objects will have been restored and put back into their previous state. You can use this method to read any high-level app data you saved in the application:willEncodeRestorableStateWithCoder: method and apply it to your app.

func application(application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [AnyObject], coder: NSCoder) -> UIViewController? {
}

During state restoration, when UIKit encounters a view controller without a restoration class, it calls this method to ask for the corresponding view controller object. Your implementation of this method should create (or find) the corresponding view controller object and return it. If your app delegate does not provide the view controller, return nil.

It is not always necessary to create a new view controller object in your implementation of this method. You can also return an existing view controller object that was created by another means. For example, you would always return the existing view controllers loaded from your app’s main storyboard file rather than create new objects.

I already told, that state saving is ViewController based. Generally it means, that you should mark needed ViewControllers for restoration by adding restoration identifiers. It can be done using interface builder or code. To mark code based ViewController for restoration you must set restorationIdentifier and restorationClass in viewDidLoad() method. Right after calling super.viewDidLoad().

override func viewDidLoad() {
    super.viewDidLoad()
    
    restorationIdentifier = "MyViewControllerRestorationId"
    restorationClass = MyViewController.self
  }

If your app is Storyboard based, then you can do it from XCode as it shown on the fig. 1, by inputing the Restoration ID or just putting a Use Storyboard ID checkmark. Actually, in the most of cases i suggest second option;

title for image

fig. 1

Implement save/restore methods

Every ViewController with a restoration identifier will receive a call encodeRestorableStateWithCoder(_:) of the UIStateRestoring protocol when the app is saved. When the app is restored, ViewController will receive decodeRestorableStateWithCoder(_:) call. Though you may need to equip these methods with your custom logic.

  • Recreate ViewControllers and Views
    After navigation stack and view controllers recreation you may need to restore your views, present some data or e. t. c.

Important! Do not save your model/view on state saving/restoration!

UIViewController conforms to UIStateRestoring protocol. If your ViewController has Restoration ID, UIKit will call the following method to save state, when application goes to background:

override func encodeRestorableStateWithCoder(coder: NSCoder) {
  // Save all the data, that you need for restoring. For instance, i need to save only one number.
  if let someNumberYouNeedForRestoring = number {
    coder.encodeInteger(someNumberYouNeedForRestoring, forKey: "number")
  }
  // It's important to call `super` because `UIKit` does a great part of restoration job for you
  super.encodeRestorableStateWithCoder(coder)
}

When UIKit will attemt to restore, it will call

override func decodeRestorableStateWithCoder(coder: NSCoder) {
  someNumberYouNeedForRestoring = coder.decodeIntegerForKey("number")
  super.decodeRestorableStateWithCoder(coder)
}

Once you’ve decoded stored objects, applicationFinishedRestoringState() method will be fired.

override func applicationFinishedRestoringState() {
  // Final configuration goes here. 
  // Load images, reload data, e. t. c.
}

To restore code-based ViewControllers use viewControllerWithRestorationIdentifierPath(_:coder:) method.

static func viewControllerWithRestorationIdentifierPath(identifierComponents: [AnyObject], 
      coder: NSCoder) -> UIViewController? {
    let viewController = YourNeededViewController()
    return viewController
  }

Tips and Tricks

Apple strongly recommens to follow these guidelines when implementing state restoration:

As you add support for state preservation and restoration to your app, consider the following guidelines:

  • Encode version information along with the rest of your app’s state. During the preservation process, it is recommended that you encode a version string or number that identifies the current revision of your app’s user interface. You can encode this state in the application:willEncodeRestorableStateWithCoder: method of your app delegate. When your app delegate’s application:shouldRestoreApplicationState: method is called, you can retrieve this information from the provided coder and use it to determine if state preservation is possible.

  • Do not include objects from your data model in your app’s state. Apps should continue to save their data separately in iCloud or to local files on disk. Never use the state restoration mechanism to save that data. Preserved interface data may be deleted if problems occur during a restore operation. Therefore, any preservation-related data you write to disk should be considered purgeable.

  • The state preservation system expects you to use view controllers in the ways they were designed to be used. The view controller hierarchy is created through a combination of view controller containment and by presenting one view controller from another. If your app displays the view of a view controller by another means—for example, by adding it to another view without creating a containment relationship between the corresponding view controllers—the preservation system will not be able to find your view controller to preserve it.

  • Remember that you might not want to preserve all view controllers. In some cases, it might not make sense to preserve a view controller. For example, if the user left your app while it was displaying a view controller to change the user’s password, you might want to cancel the operation and restore the app to the previous screen. In such a case, you would not preserve the view controller that asks for the new password information.

  • Avoid swapping view controller classes during the restoration process. The state preservation system encodes the class of the view controllers it preserves. During restoration, if your app returns an object whose class does not match (or is not a subclass of) the original object, the system does not ask the view controller to decode any state information. Thus, swapping out the old view controller for a completely different one does not restore the full state of the object.

  • 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. (As a safety precaution, the system also deletes preserved state if the app crashes twice during launch.) 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.

Where to go from here?

First of all, this article does not cover View states preserving. This topic is well covered here. For better understanding i recommend you to check the sources mentioned in the bottom of the article.

Conclusion

Saving application state across launches is relatively cheap, but very important thing, if your goal is creating high quality applications. UI state restoration is a tool with wide possibilities. You can yse it to remember tge selected tab, or to fully recreate the UI across launches.

Sources