Sample Code

Restoring Your App’s State

Preserve and restore information related to the user’s current activities.

Download

Overview

This sample project demonstrates how to preserve your app’s state information and restore your app to its previous state on subsequent launches. When using your app, the user takes actions that affect the user interface. For example, the user might view a specific page of information, and after the user exits the app, the operating system might terminate the app to free up the resources it holds. During the subsequent launch, restoring your interface to the previous interaction point provides continuity for the user, and lets them finish active tasks quickly.

This sample app demonstrates the use of state preservation and restoration for scenarios where your app is likely to be interrupted. The sample project manages a set of notes. Each note has a title and content. The user can create, edit, and reorder these notes. The project shows how to preserve and restore a given note in its DetailViewController, restoring the note’s title and content.

The sample supports two different state preservation approaches. In iOS 13 and later, apps save state for each window scene using NSUserActivity objects. In iOS 12 and earlier, apps preserve the state of their user interface by saving and restoring the configuration of view controllers.

For additional information about state restoration, see Preserving Your App's UI Across Launches.

Configure the Sample Code Project

In Xcode, select your development team on the macOS target’s General tab.

Enable State Preservation and Restoration for Your App

For scene-based apps, UIKit asks each scene to save its state information using an NSUserActivity object. In your own apps, you use the activity object to store information needed to recreate your scene’s interface and restore the content of that interface. If your app doesn’t support scenes, use the view-controller-based state restoration process to preserve the state of your interface instead.

To provide the needed activity object, the sample implements the stateRestorationActivityForScene: method of its scene delegate object. Implementing this method tells the system that the sample supports user-activity-based state restoration. The implementation of this method returns the activity object already stored in the scene’s userActivity property, which the sample populates when the scene becomes inactive.

// This is the NSUserActivity that will be used to restore state when the scene reconnects.
func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? {
    return scene.userActivity
}

For view-controller-based state restoration, this sample opts-in to state preservation and restoration using the app delegate’s application:shouldSaveApplicationState: and application:shouldRestoreApplicationState: methods. Both methods return a Bool value that indicates whether the step should occur. This sample returns true for both functions.

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

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

Preserve and Restore the App State with an Activity Object

Scene-based state restoration is the recommended way to restore the app’s user interface. An NSUserActivity object captures the app’s state at the current moment in time. For example, you might include information about the data your app is currently displaying. The system saves the object you provide and returns it to your app the next time it launches.

The sample creates a new NSUserActivity object when the user closes the app or the app enters the background. At that time, the applicationWillResignActive: method of the scene delegate creates an activity object only if a detail view controller is visible. If no detail view controller is visible, the app is already displaying the default user interface, and no activity object is necessary.

func sceneWillResignActive(_ scene: UIScene) {
    if let navController = window!.rootViewController as? UINavigationController {
        if let detailViewController = navController.viewControllers.last as? DetailViewController {
            // Fetch the user activity from our detail view controller so restore for later.
            scene.userActivity = detailViewController.detailUserActivity
        }
    }
}

When the user launches the app again, the sample’s scene:willConnectToSession:options: method checks for the presence of an activity object. If one is present, the method configures the detail view controller specified by that activity object.

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Do we have an activity to restore?
    if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
        // Setup the detail view controller with it's restoration activity.
        if !configure(window: window, with: userActivity) {
            print("Failed to restore DetailViewController from \(userActivity)")
        }
    }
}

Preserve and Restore the App State Using View Controllers

If your app does not yet support scenes, you preserve your app’s state by saving the state of its view controller hierarchy. View controllers adopt the UIStateRestoring protocol, which defines methods for saving custom state information to an archive and restoring that information later.

To specify which of your app’s view controllers you want to save, assign a restoration identifier to that view controller. A restoration identifier is a string that UIKit uses to identify a view controller or other user interface element. The identifiers you assign to your view controllers must be unique. You may specify them in Interface Builder or in code.

The sample assigns a restoration ID assigned to each view controller in the storyboard file. You can find this information by selecting the view controller and looking in the Identity Inspector. The Storyboard ID for that view controller is usually the same as the Restoration ID.

This sample saves the state information in the detail view controller’s encodeRestorableStateWithCoder: method, and it restores that state in the restoreStateWithCoder: method. Because it already encapsulates the view controller’s state in an NSUserActivity object, the implementations of these methods operate on the existing activity object. Calling super from these methods is required and allows UIKit to restore the rest of the view controller’s inherited state.

override func encodeRestorableState(with coder: NSCoder) {
    super.encodeRestorableState(with: coder)

    let encodedActivity = NSUserActivityEncoder(detailUserActivity)
    coder.encode(encodedActivity, forKey: DetailViewController.restoreActivityKey)
}

override func decodeRestorableState(with coder: NSCoder) {
    super.decodeRestorableState(with: coder)
    
    if coder.containsValue(forKey: DetailViewController.restoreActivityKey) {
        if let decodedActivity = coder.decodeObject(forKey: DetailViewController.restoreActivityKey) as? NSUserActivityEncoder {
            if let activityUserInfo = decodedActivity.userActivity.userInfo {
                restoreItemInterface(activityUserInfo)
            }
        }
    }
}

Test State Restoration on a Device

When debugging your project 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. In addition, the system also deletes preserved state if the app crashes at launch time. If you want to test your app’s ability to restore its state, do not use the app switcher to kill the the app during debugging. Instead, use Xcode to kill the app, or kill the app programmatically. One technique is to suspend your app using the Home button, and then stop the debugger in Xcode. When you launch the app again using Xcode, UIKit initiates the state restoration process.

See Also

Interface Restoration

Preserving Your App's UI Across Launches

Return your app to its previous state after it is terminated by the system.

UIViewControllerRestoration

The methods that objects adopt so that they can act as a "restoration class” for view controllers during state restoration.

UIObjectRestoration

The interface that restoration classes use to restore preserved objects.

UIStateRestoring

Methods for adding objects to your state restoration archives.