I've seen this question in various forms without a clear answer. I'm going to ask and answer here. My app needs to do work on startup... inits, a few network calls, login, etc. I don't want my main view controller working until that's done. What's a good pattern for doing this?
Requirements:
- iOS5+, storyboards, ARC.
- the main vc's view cannot appear until the startup work is done.
- want a choice of transition styles to the main vc when the startup work is done.
- want to do as much layout as possible in storyboard.
- the code should be cleanly contained somewhere obvious.
Another solution when using a navigation controller.
Your navigation controller being the initial view controller, set your main controller as the navigation controller root's view.
Add your loading controller to the storyboard and link with a named segue of style modal.
In your main controller's viewWillAppear trigger the segue (only once per app run).
It will not work if you trigger the segue in viewDidLoad probably because the navigation controller's animation is not done yet and you will receive an
unbalanced calls to begin/end appearance transitions
EDIT, July 2017 Since the first writing, I've changed my practice to one where I give the start up tasks to their own view controller. In that VC, I check startup conditions, present a "busy" UI if needed, etc. Based on the state at startup, I set the window's root VC.
In addition to solving the OP problem, this approach has the additional benefits of giving better control of the startup UI and UI transitions. Here's how to do it:
In your main storyboard, add a new VC, called
LaunchViewController
and make it the app's initial vc. Give your app's "real" initial vc an identifier like "AppUI" (identifiers are on the Identity tab in IB).Identify other vcs that are the starts of main UI flows (e.g. Signup/Login, Tutorial, and so on) and give these descriptive identifiers, too. (Some prefer to keep each flow in it's own storyboard. That's a good practice, IMO).
One other nice optional idea: give your app's launch storyboard's vc an identifier, too (like "LaunchVC"), so that you can grab it and use it's view during startup. This will provide a seamless experience for the user during launch and while you do your startup tasks.
Here's what my
LaunchViewController
looks like....The original answer below, though I prefer my current approach
Let's express the app's readiness to run the main vc with a boolean, something like:
In the AppStartupViewController.m, when the readyToRun conditions have been met, it can dismiss itself:
Now, whenever the app becomes active, it can check for readiness to run, and present the AppStartupViewController if it's needed. In AppDelegate.h
That's mostly the answer, but there is one hitch. Unfortunately the main vc gets loaded (that's okay) and gets a viewWillAppear: message (not okay) before the AppStartupViewController is presented. It means we have to spread a little extra startup logic, like this, in MainViewController.m:
I hope this is helpful.