My pal Soroush recently floated the idea of implementing reusable behaviors that pertain to a view controller’s lifecycle – such as analytics logging – using additional view controllers. There’s a lot to like about this idea; since it’s easy to compose view controllers, you don’t need to worry about adding custom storage to each of your view controller subclasses or manually overriding and hooking into each their lifecycle methods.
His post ended up being a bit of a divisive one. While view controller containment seems universally liked, using a view controller to model logic that doesn’t have its own associated view can feel wrong. While I empathize with this sentiment, I find the concept of “lifecycle behaviors” so enticing that I’m willing to forgive their implementation perhaps feeling a bit unorthodox.
First, let’s define what exactly a lifecycle behavior would look like. Any of UIViewController’s standard lifecycle methods could theoretically be worth hooking into, which yields a protocol that looks something like the following:
In order for a behavior to optionally implement whichever lifecycle methods are pertinent, we can provide empty implementations by default:
Let’s say that our app uses UINavigationController and that the navigation bar is visible by default, but the occasional view controller needs to hide it. Modeling this behavior in a concrete ViewControllerLifecyleBehavior is trivial:
Now, how do we actually integrate these behaviors into our view controller’s lifecycle? Since view controllers forward their lifecycle methods to their children, creating a child view controller is the easiest way to hook in:
Lastly, adding this behavior to one of our custom view controllers is as simple as:
And that’s all there is to it. Sure, the implementation might not be using view controllers in exactly the way that UIKit intends, but when has that stopped us before? Perhaps future enhancements to the language and SDK will allow our addBehaviors(behaviors: [ViewControllerLifecycleBehavior]) method to be more idiomatically implemented (increased dynamism could better facilitate aspect-oriented programming). But we won’t need to change all of our individual UIViewController subclasses if and when this happens.