The initializer-based dependency injection is likely the safest way of creating view controllers. However, it is not compatible with storyboards because controllers are created by the framework and dependencies can only be injected in properties. If the storyboards are still required, how can the risk of getting not fully configured controllers be minimized?

Restrict the view controller creation to one place, whose single responsibility is the creation of objects. The common design pattern called Abstract Factory can be applied here.

When using a factory, the view controller dependencies that have to be injected as properties reach the view controller via two paths:

The long-lived view controller dependencies become factory dependencies. They are first passed to the factory in its initializer and then to the view controller properties after the object construction.

The short-lived dependencies are passed to the factory creation methods as parameters.

Example

The PhotoDetailViewController has two dependencies: a photo and a context. The long-lived context becomes a factory dependency. The short-lived photo becomes a factory method parameter.

Now all clients that need to create PhotoDetailViewController instances won’t forget to pass a photo and a context to them.

Testability

Even if the view controllers are created in one place without a factory, the above-mentioned technique can improve the testability. Imagine a routing object responsible for the navigation from one screen to another. Wireframe from the VIPER architecture could be taken as an example.

By injecting an abstract factory into the Wireframe, it becomes trivial to test if the Wireframe creates the proper view controllers.

Because the ViewControllerFactory is a protocol, a spy implementing it can be injected instead of the StoryboardViewControllerFactory for testing the Wireframe.

Conclusion

When the initializer-based dependency injection can’t be accomplished, like with storyboards, the next-best alternative for setting dependencies is the property-based injection. To minimize the risk of working with not fully configured objects, the view controller creation should be constrained within one place. Code testability can be further improved by implementing the central creation point using the Abstract Factory pattern.

Related Articles