In this session, Greg Heo explores the various uses and abuses of ViewControllers in search of a better way of structuring applications.
Hold the Model
The ViewController is a container for views. It holds views - in particular, they hold references to view; either freshly created views or ones that are created using outlets in your storyboard or nib.
A hotly-debated topic is what ViewControllers are supposed to do. On one end of the spectrum, there are people who want to put everything into the ViewController, then on the other end, there are minimalists who think view controllers should do as little as possible and we should move the code somewhere else.
It’s acceptable to hold the belief that ViewControllers should not have business logic, but not so only for the sake of having a small ViewController. A fix for making a ViewController small is to use MVVM architecture (model view view model), but poorly done MVVM merely shifts the bulk of the code from the view controller to the view model.
Get more development news like this
Let’s try to be more thoughtful about a solution, and explore the pros and cons to each.
With traditional Model View Controller, the controller coordinates between the view and the model. In Cocoa, that often means that the view controller manages the model in addition to the view.
View controllers also coordinate views. In a simple example, a button has a tap action that results in the text being set. Where should this code reside? Do we need another level of indirection, or another controller to handle this?
View Appearance Callbacks
ViewControllers have view appearance callbacks. This includes viewDidLoad, viewDid/willAppear, and also Disappear.
A ViewController manages the lifecycle of loading the view and putting things on and off screen.
viewDidLoad is a very popular method to put in view setup codes, and as a result, you often have very long
viewDidLoad methods that set up an entire view hierarchy, and add constraints.
There are also methods to detect the change in rotation, such as the view/did layout subviews. Often you’ll find code in here that does recalculations of frame and bounds and adjustment of layout constraints.
- View Editing - With ViewControllers, there’s an editing state. You can do multiple selections, but that’s actually a property on ViewController, and not TableViewControllers specifically.
- Search - There’s a property for SearchDisplayController.
- Title - The title is in the navigation controller.
- Style - Modal, etc.
Transitions - custom transitions.
Rotations - Handling rotation is a top level physical device event. This rotation is handled in the ViewController.
- Navigation - Navigation, TabBar, and Split View Controllers are all ViewControllers.
Let’s look at MVVM - the model, view model, view controller, and view. Here, the view controller and the view are the same. You message the view controller directly from outside, but not the view.
The view model will handle presentation logic and business logic. On navigation, in the context of MVVM, where it goes is still up in the air.
VIPER stands for View, Interactor, Presenter, Entity, and Routing. The view is simply a view and has visible elements, the presenter has some model aspects, and it has all of the presentation logic, knows how to display the data, and also knows how to handle things like button taps and text entry.
The interactor has the business logic, and is much like the controller, as in it works between the presenter and entity.
The routing module handles navigation. This starts to look better because we are following more of the single responsibility principle here.
Riblets, used by Uber in their app, is similar to VIPER, but there are differences.
You can read about this on the blog post. The router and the interactor are required, and perhaps the model as well.
This is a very iOS centric way of designing things, but on Android, that is not always the case, and navigation and business logic can be the thing that doesn’t have a view. Imagine if you have a background operation on iOS. There may not be a presenter or a view around, but we might want to send a user to a particular code path.
You can also imagine how part of single responsibility principle is also to make things more testable.
Functional reactive programming libraries like RxSwift and ReactiveCocoa take a very different approach to handling data. We’re used to an event model: the button was tapped, data came in over the network, somebody touched something in here and it’s events and we need to write action methods to handle those things.
The reactive model is different in that there are streams of data. There will be a time-based stream of clicks that will be just observed.
Key Value Observing is a very different kind of pattern and it’s very different from standard MVC.
There are streams of data, observers on those streams, and bits of business logic, or its independent functions. Each bit of business logic can do its own thing with the view and the model however it wants. You can even imagine an entire MVC universe inside each one of these things.
I think there are probably an infinite number of ways to look at view controllers, but it’s always up to you to think about what angle you want to take in deciding the circle of influence of your own view controllers.
View controllers do a lot of things by default in iOS and it’s our job to be thoughtful about app architecture to pull in those circles at the edge of the view controller and define what it is exactly we want them to do.
Which architecture are you using at Instagram?
I would say it’s a mix because the app is very old and a lot of people worked on it over the years. There’s a mix of MVC, traditional massive view controller, MVCC, and MVVM.
When you have the autonomy to decide if you’re working on your own projects, what is your preferred approach?
My own autonomous project was my preferred approach. I’m a big fan of RxSwift and ReactiveSwift these days. I like the way that it models everything, network data, user taps, text entry, etc, and it funnels it together into a similar interface.