UI state restoration dead in iOS 13?

I am implementing multi-window scene support in iOS 13 and, the best I can gather from WWDC and documentation, it looks like the system-provided UI state restoration machinery that we enjoyed in iOS 12 no longer works (for multi-window apps). We are supposed to use NSUserActivity to save/restore state on our own. Am I understanding that correctly? I am confused on a couple points and looking for some guidance:


1. The documentation for UISceneDelegate says that "User activity objects are meant for recording what the user was doing, so you don't need to save the state of your scene's UI." Doesn't this provide a worse user experience that the old state-restoration machinery? Why would we not want to save fine-grained UI state like scroll position, view controller hierarchies, etc. to provide a better experience for users when they come back to our app? Asking each dev to implement their own state save/restore mechanism seems like a step backwards.


2. Is there some way to invoke the system-provided UI state restoration mechanism on a given scene that would give us an NSData archive that we could save in the NSUserActivity's userInfo dictionary? Likewise for restoring UI state from an NSUserActivity's userInfo dictionary.


Hoping for some additional insight. Just seems like a move backward to jettison all of the UI state restoration work in favor of a roll-your-own solution using NSUserActivity.


Thanks,

Jonathan

Replies

I Would also like to know the answer to that. State restoration before did a lot of things like connect the same objects, so you could have multiple view controllers holding a reference to the same object, or have a delegate property connect upon restore to the correctly restored view controller. That is lost now with iOS 13 user activity scheme.

If your app supports multiple windows, automatic state restoration based on the storyboard has no clear place to start. But I don't see what stops you from using view controller based state restoration on a per-window basis. The scene session's `persistentIdentifier` identifies the hierarchy, as the WWDC videos explain. Or am I wrong about that?

Woah...just looking into this myself. They briefly gloss over using NSUserActivity for state restoration in the WWDC video about supporting multiple windows on iPad....


From what I can tell, this throws the entire working fairly easy to use state restoration machinary out the window? Or am I missing something? Returning a NSUserActivity from a sceneDelegate's method -stateRestorationActivityForScene: seems to prevent the AppDelegate from receving -application:shouldSaveApplicationState:


So, developers are left to reimplement the entire state restoration machinary? What a mess?


In the sample I see relating to this, there's a SceneDelegate method that looks like this:


func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
            if !configure(window: window, with: userActivity) {
                print("Failed to restore from \(userActivity)")
            }
        }

        // If there were no user activities, we don't have to do anything.
        // The `window` property will automatically be loaded with the storyboard's initial view controller.
    }


So I see there's UIConnectionOptions that has a property:


@property (nonatomic, readonly, copy) NSSet *userActivities;


Which is documented:


@property (nonatomic, readonly, copy) NSSet<NSUserActivity *> *userActivities;

If this property contains one or more NSUserActivity objects, use those objects to restore the state of your scene to its previous configuration. You create user activity objects at key moments in your scene's lifetime and register them with the system. Your activity objects specify the tasks your scene was managing. For example, if the user was browsing a web page, your activity object contains the URL of that page. Use the information in the activity object to configure your scene's UI and restore any data associated with the given task.

This property does not contain user activity objects related to Handoff. At connection time, UIKit delivers only the type of a Handoff interaction in the handoffUserActivityType property. Later, it calls additional methods of the delegate to deliver the

NSUserActivity
object itself.


---

Then there's also a stateRestorationActivity property on UISceneSession. Is the scene session's activity in the connection options set? What's the strategy here? Seems confusing and wasn't explained too well. The multiple window sample just stores a string which maps to an image name and loads it in a view controller that displays a UIimageView and pushes it on a navigation stack. Pretty simple, but many apps have deep navigation stacks, tab bars, ect., and the old state restoration API seemed a million times superior?


Not sure why they didn't just add a UIWindowController class for multiple windows and just have window controllers conforms to UIStateRestoring....

This is an excellent question. I'm wondering about exactly the same thing. The provided samples are extremely simplistic.


Dear Apple engineers, could we get a reply about what the suggested strategy is when it comes to real life applications with deep view controller hierarchies, tab bars, etc?


Thank you!

Reviving this thread as I'm currently struggling with state restoration in an app with multi-scene support.

I'm assigning a stateRestorationActivityForScene in the SceneDelegate and checking for it when (re-) connecting a scene. View controllers have userActivity assigned to this activity and their updateUserActivityState method is called when the scene is disconnected.

So far so good, and I think I've understood the concept of storing state-relevant information in the activity. What really makees me head spin is restoring the view hierarchy: Using a split view with two navigation stacks, master / detail view controller etc. make this quite complex, especially with support on iPad and iPhone... The legacy state restoration was taking care of the complete hierarchy and I could focus on view controller specific settings.

Do I really have to mimic the whole storyboard setup, tracking and replaying all transitions etc.? I can hardly believe this step back, what did I miss?

Massively confused, Mattes