I don’t use Storyboards or State Restoration in my own projects, but I have looked into them in detail at one time or another. From what I understand about both of these APIs, the life cycle for view controllers looks something like this:
1. Something happens in the app. 2. UIKit triggers the initialization of a view controller. 3. The initialized view controller is prepared just prior to being presented. 4. The view controller is presented.
It’s usually inside of Step 3 that depedencies are passed into the new view controller. In the case of State Restoration and a primary Storyboard, for example, the new view controller is given an opportunity to resume its previous state inside:
func decodeRestorableStateWithCoder(_ coder: NSCoder)
Or in the case of a Storyboard segue, the following method is called:
func prepareForSegue(_ segue: UIStoryboardSegue, sender sender: AnyObject?)
In either case, the new view controller is initialized before it receives its dependencies. But this order of operations is the opposite of what Swift encourages, particularly with regard to optionals.
Swift’s optionals make my code safer. They make it nearly impossible, with good coding practices, to make a mistake when reasoning about whether or not an element will be non-nil at run time. But optionals can be really annoying, too, for the same reason that they’re safer. As
if let and
guard checks build up like snowdrifts at the edges of my code, I find myself gravitating towards API designs that eliminate unnecessary optionals. I look for opportunities to “convert” an optional property to a non-optional one by providing a guaranteed non-nil value for it in the view controller’s initializer.
Thus, my Swift code tends towards view controller life cycles like this:
1. Something happens in my app. 2. I initialize my view controller programmatically, passing in dependencies. 3. My view controller is presented.
Clearly this pattern in Swift is mismatched with the requirements imposed by Storyboards and State Restoration. Swift pushes dependencies towards the
init() method, while Storyboards and State Restoration push them into later stages in the life cycle. I have been able to handle this so far by avoiding Storyboards and State Restoration in favor of custom, programmatic alternatives. But I worry that Apple’s winds are blowing in the other direction and someday I’ll have to change up for a new design pattern.
What do you do in your code? Do you avoid Storyboards and State Restoration like I do? Do you have cleverer ways of dealing with the mismatch?