State Restoration Tutorial: Getting Started

In this state restoration tutorial, learn how to use Apple’s State Restoration APIs to enhance a user’s experience of your app. By Luke Parham.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

The UIStateRestoring Protocol

When it comes to implementing state restoration, UIKit does a lot for you, but your app is responsible for a few things on its own:

  1. Telling UIKit it wants to participate in state restoration, which you did in your app delegate.
  2. Telling UIKit which view controllers and views should be preserved and restored. You took care of this by assigning restoration identifiers to your view controllers.
  3. Encoding and decoding any relevant data necessary to reconstruct the view controller to its previous state. You haven’t done this yet, but that’s where the UIStateRestoring protocol comes in.

Every view controller with a restoration identifier will receive a call to encodeRestorableStateWithCoder(_:) of the UIStateRestoring protocol when the app is saved. Additionally, the view controller will receive a call to decodeRestorableStateWithCoder(_:) when the app is restored.

To complete the restoration flow, you need to add logic to encode and decode your view controllers. While this part of the process is probably the most time-consuming, the concepts are relatively straightforward. You’d usually write an extension to add conformance to a protocol, but UIKit automatically registers view controllers to conform to UIStateRestoring — you merely need to override the appropriate methods.

Open PetDetailsViewController.swift and add the following code to the end of the class:

override func encodeRestorableStateWithCoder(coder: NSCoder) {
  //1
  if let petId = petId {
    coder.encodeInteger(petId, forKey: "petId")
  }
  
  //2
  super.encodeRestorableStateWithCoder(coder)
}

Here’s what’s going on in the code above:

  1. If an ID exists for your current cat, save it using the provided encoder so you can retrieve it later.
  2. Make sure to call super so the rest of the inherited state restoration functionality will happen as expected.

With these few changes, your app now saves the current cat’s information. Note that you didn’t actually save the cat model object, but rather the ID you can use later to get the cat object. You use this same concept when saving your selected cats in MatchedPetsCollectionViewController.

Apple is quite clear that state restoration is only for archiving information needed to create view hierarchies and return the app to its original state. It’s tempting to use the provided coders to save and restore simple model data whenever the app goes into the background, but iOS discards all archived data any time state restoration fails or the user kills the app. Since your user won’t be terribly happy to start back at square one each time they restart the app, it’s best to follow Apple’s advice and only save state restoration using this tactic.

Now that you’ve implemented encoding in PetDetailsViewController.swift, you can add the corresponding decoding method below:

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

Here you decode the ID and set it back to the view controller’s petId property.

The UIStateRestoring protocol provides applicationFinishedRestoringState() for additional configuration steps once you’ve decoded your view controller’s objects.

Add the following code to PetDetailsViewController.swift:

override func applicationFinishedRestoringState() {
  guard let petId = petId else { return }
  currentPet = MatchedPetsManager.sharedManager.petForId(petId)
}

This sets up the current pet based on the decoded pet ID and completes the restoration of the view controller. You could, of course, do this in decodeRestorableStateWithCoder(_:), but it’s best to keep the logic separate since it can get unwieldy when it’s all bundled together.

Build and run your app; navigate to a pet’s detail view and trigger the save sequence by backgrounding the app then killing it via Xcode. Re-launch the app and verify that your same furry friend appears as expected:

Details

You’ve learned how to restore view controllers created via storyboards. But what about view controllers that you create in code? To restore storyboard-created views at run-time, all UIKit has to do is find them in the main storyboard. Fortunately, it’s almost as easy to restore code-based view controllers.

Restoring Code-based View Controllers

The view controller PetEditViewController is created entirely from code; it’s used to edit a cat’s name and age. You’ll use this to learn how to restore code-created controllers.

Build and run your app; navigate to a cat’s detail view then click Edit. Modify the cat’s name but don’t save your change, like so:

Edit

Now background the app and kill it via Xcode to trigger the save sequence. Re-launch the app, and iOS will return you to the pet detail view instead of the edit view:

Details

Just as you did for the view controllers built in Interface Builder, you’ll need to provide a restoration ID for the view controller and add the encode and decode UIStateRestoring protocol methods to properly restore the state.

Take a look at PetEditViewController.swift; you’ll notice the encode and decode methods already exist. The logic is similar to the encode and decode methods you implemented in the last section, but with a few extra properties.

It’s a straightforward process to assign the restoration identifier manually. Add the following to viewDidLoad() right after the call to super:

restorationIdentifier = "PetEditViewController"

This assigns a unique ID to the restorationIdentifier view controller property.

During the state restoration process, UIKit needs to know where to get the view controller reference. Add the following code just below the point where you assign restorationIdentifier:

restorationClass = PetEditViewController.self

This sets up PetEditViewController as the restoration class responsible for instantiating the view controller. Restoration classes must adopt the UIViewControllerRestoration protocol and implement the required restoration method. To that end, add the following extension to the end of PetEditViewController.swift:

extension PetEditViewController: UIViewControllerRestoration {
  static func viewControllerWithRestorationIdentifierPath(identifierComponents: [AnyObject], 
      coder: NSCoder) -> UIViewController? {
    let vc = PetEditViewController()
    return vc
  }
}

This implements the required UIViewControllerRestoration protocol method to return an instance of the class. Now that UIKit has a copy of the object it’s looking for, iOS can call the encode and decode methods and restore the state.

Build and run your app; navigate to a cat’s edit view. Change the cat’s name as you did before, but don’t save your change, then background the app and kill it via Xcode. Re-launch your app and verify all the work you did to come up with a great unique name for your furry friend was not all in vain!

Edit