I'd like to do state restoration in my app that doesn't use story boards. I'm seeing my primary app ViewController instantiated twice during state restoration - how do you ensure that it's only created once?
The way I understand the flow, application:willFinishLaunchingWithOptions
and application:didFinishLaunchingWithOptions
would use a commonInit
method which would setup the applications UIWindow and its rootViewController. In my case, the rootViewController is a UINavigationController with a class named 'MyMainViewController' serving as UINavigation's rootViewController.
Along side this I'm also handling willEncodeRestorableStateWithCoder
and didDecodeRestorableStateWithCoder
, respectively. But it seems that by the time I get to my didDecodeRestorableStateWithCoder
, I see two separate instances of MyMainViewController created.
What's the way to ensure that only one UIViewController is created during restoration?
Order of calls during restoration:
- Create new instance MyMainViewController (#1) via application:willFinishLaunchingWithOptions:
- MyMainViewController's viewControllerWithRestorationIdentifierPath:coder invoked and MainViewController is restored (#2)
- application:didDecodeRestorableStateWithCoder: is called and UINavigationController is decoded and assigned to self.window
Here's what I'm doing in my AppDelegate:
NSString * const kRootViewControllerKey = @"RootViewControllerKey";
- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self commonInitWithOptions:launchOptions];
return YES;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self commonInitWithOptions:launchOptions];
return YES;
}
- (void)commonInitWithOptions:(NSDictionary *)launchOptions {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^ {
// While this will be called only once during the lifetype of the app, when the process is killed
// and restarted, I wind up with an instance of MyMainViewController created first from here
// and then once again, during MyMainViewController's viewControllerWithRestorationIdentifierPath:coder
// that's invoked later on.
UIViewController *rootViewController = [MyMainViewController alloc] init];
UINavigationController *aNavController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
aNavController.navigationBarHidden = YES;
aNavController.restorationIdentifier = NSStringFromClass([aNavController class]);
UIWindow *aWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
aWindow.rootViewController = aNavController;
aWindow.restorationIdentifier = NSStringFromClass([window class]);
self.window = aWindow;
});
}
// Encode app delegate level state restoration data
- (void)application:(UIApplication *)application willEncodeRestorableStateWithCoder:(NSCoder *)coder {
[coder encodeObject:self.window.rootViewController forKey:kRootViewControllerKey];
}
// Decode app delegate level state restoration data
- (void)application:(UIApplication *)application didDecodeRestorableStateWithCoder:(NSCoder *)coder {
// Find the preserved root view controller and restore with it
UINavigationController *navControlller = [coder decodeObjectForKey:kRootViewControllerKey];
if (navControlller) {
self.window.rootViewController = navControlller;
}
}