久美子
久美子

Reputation: 121

How can I get app State restoration to work?

I have a simple application of 2 table view controllers, and I am trying to preserve the first table view controller screen across application launches. The first table view gets populated dynamically when you select a cell from the second table view. The way I have been testing this is by adding a new cell, minimizing the app, clicking stop from Xcode then pressing the play button on Xcode. However it doesn't do anything, I have been stuck on it for few days and I can't figure out what I am doing wrong, any help would be very appreciated.

I have my restorationId set:

 override func viewDidLoad() {
     super.viewDidLoad()

     restorationIdentifier = "MainTableViewController"        
}

This is my AppDelegate:

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        self.window?.makeKeyAndVisible()
        return true
    }

    // MARK: UISceneSession Lifecycle

    @available(iOS 13.0, *)
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    @available(iOS 13.0, *)
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

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

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

    func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        window = UIWindow(frame: UIScreen.main.bounds)
        window?.makeKeyAndVisible()
        let viewController = MainTableViewController()
        let navC = UINavigationController(rootViewController: viewController)
        navC.restorationIdentifier = "MainTableViewController"
        window?.rootViewController = navC
        return true

    }

    func application(_ application: UIApplication, didDecodeRestorableStateWith coder: NSCoder) {
        UIApplication.shared.extendStateRestoration()
        DispatchQueue.main.async {
            UIApplication.shared.completeStateRestoration()
        }
    }
}


In my 1st table view controller I have this:

extension MainTableViewController: UIViewControllerRestoration {

    static let activityIdentifierKey = "MainActivity"

    override func encodeRestorableState(with coder: NSCoder) {
        super.encodeRestorableState(with: coder)
        coder.encode(exchangeRates, forKey: MainTableViewController.activityIdentifierKey)
    }

    override func decodeRestorableState(with coder: NSCoder) {
        super.decodeRestorableState(with: coder)
        if let returnedPersons = coder.decodeObject(forKey: MainTableViewController.activityIdentifierKey) as? [Person]{
            persons = returnedPersons
        }
    }

    override func applicationFinishedRestoringState() {
        tableView.reloadData()
    }

    static func viewController(withRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
        guard let restoredPersons = coder.decodeObject(forKey: "MainActivity") as? [Persons] else {
            print("decoding User Detail")
            return nil
        }

        let vc = MainTableViewController()
        vc.persons = restoredPersons
        return vc
    }
}

I have tried running the application on iOS 13.2, iOS 12, and iOS 11

Upvotes: 3

Views: 2647

Answers (2)

Stanley Ejechi
Stanley Ejechi

Reputation: 61

Use this method:

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

instead of the deprecated method:

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

Upvotes: 4

Bill
Bill

Reputation: 45418

This is probably not a code issue - it's just the way state restoration works in the simulator. When you stop your app in Xcode, the app process just abruptly dies, and there's no time to log state information.

It's a pain, but in order to test state restoration in the simulator, you need to close the app in the simulator, just as you'd do on hardware:

  1. In the simulator, press the home button twice (Shift-Cmd-H).
  2. You'll see the task switcher. Drag your app up so it closes.
  3. Restart the app in Xcode.

Your state restoration code should work now.

You can verify this by putting a breakpoint in shouldSaveApplicationState. It won't get called when you stop the app from Xcode, but it will get called if you close the app within the simulator.

Upvotes: 2

Related Questions