A Better MVC, Part 5: An Evolution

Part 5 in a series on "fixing" Model-View-Controller:

  1. A Better MVC, Part 1: The Problems
  2. A Better MVC, Part 2: Fixing Encapsulation
  3. A Better MVC, Part 3: Fixing Massive View Controller
  4. A Better MVC, Part 4: Future Directions
  5. A Better MVC, Part 5: An Evolution

About five months ago, I wrote about improving MVC and fixing the “massive view controller” problem.

In the intervening months, I’ve had more time to consider this evolution and its implications, and have come to more conclusions. This post is the “update” on how my thinking has changed.

MVC isn’t a Pattern

We talk about the “MVC Design Pattern”, and I think that’s a mis-nomer. Every architectural pattern out there, from MVVM to FRP to VIPER to React to insert random architecture here deals with the primary principles of MVC.

MVC says that your data (model) is distinct from your business logic (controller), which is separate from its visual representation (view). It says that your networking code shouldn’t know about the details of your state restoration code, or that your deep link routing code doesn’t have to know about your database schema. Each module of your app should be a “black box” to the other modules.

MVC is a philosophy. It’s not a pattern, in the traditional sense of how we talk about design patterns. The philosophy of MVC says that Encapsulation Is Important™, and that building a flexible and maintainable app is most easily done if you can isolate responsibilities and restrict information to a need-to-know basis.

Controller or Coordinator?

In part 2, I implied that your UIViewController chain should be the part of your app that “owns” and controls the reason why your UI is changing. Or, in other words, that it is the “Controller” in “Model-View-Controller”.

I don’t think this is true anymore.

As I examined UIViewController and its role in apps more, I came to the conclusion that, despite its name, UIViewController is not a Controller, but a View. Almost everything that UIViewController deals with relates to view-level concepts, which heavily implies that it itself is a View, and should be treated as a View.

This means you need actual Controllers. Maybe this is where Flow Coordinators comes in. Or maybe you take a Presenter/Router approach, or you dive in to streams of values over time with FRP. These are all patterns that deal with Controller organization, whereas most discussion around MVC (including this series of posts) focuses on organizing your View layers.

Sequence or State?

In Part 2, I also said:

In general, a view controller should manage either sequence or UI, but not both.

I still believe this to be generally true, but from a different point of view. When I wrote this, I was referring to the UIViewController’s responsibilities of either owning business logic or putting stuff into its contained UIViews. As I mentioned above, I now believe that UIViewController lives firmly in the View layer of your app and shouldn’t have any business logic at all.

However, I still believe this statement is true.

If you take a look at the various kinds of UIViews that UIKit provides, you’ll notice something: They all either directly draw stuff, or compose private subviews.

I can’t think of a single UIKit element that both draws things directly and composes other subviews. It’s possible they’re there, but such a thing would be extremely odd to me.

UIButton itself owns the touch handling, but all the rendering is done by composed labels and image views.

UITextField deals with interaction and reacting to keyboard events, but all rendering is done by composed labels and image views.

You could say that UITextField itself manages “sequence” by composing other views and routing events appropriately, but leaves the details of “state” (actually drawing stuff) up to those subviews.

UIViewControllers should follow this idea too. In general, they should either:

  1. Compose child view controllers or
  2. Put data into their owned UIViews

This is the same general pattern that UIKit widgets follow, but just brought up a level to the UIViewController layer.

Of course, there are exceptions to this. There are cases where you’re writing a UIViewController that wants to both compose children and put stuff in to views. UITabBarController is an example of this: It “owns” the actual UITabBar instance, as well as composes the various children that make up the content of the tabs.

So instead of saying a UIViewController should “manage either sequence or UI”, perhaps a better way of saying it would be that a UIViewController should either compose children or put stuff in to UIViews (with the understanding that this is a guideline, and not a rule).

The Evolution

I’ve given a presentation on the whole evolution of these concepts at a couple of recent conferences, and will continue to do so through this year. The video is about 35 minutes long, so I’d encourage you to check it out:

Related️️ Posts️

A Better MVC, Part 4: Future Directions
A Better MVC, Part 3: Fixing Massive View Controller
A Better MVC, Part 2: Fixing Encapsulation
A Better MVC, Part 1: The Problems