Breaking the MVC Pattern: Models in Views

The Model-View-Controller (MVC) pattern makes it very clear that Models and Views should never talk to each other. Here is a great diagram of the MVC pattern from Stanford’s CS193P first lecture:

Model View Controller

However, in practice, I’ve found that it is not always a clear-cut distinction as the above image suggests. As a result, I’ve been struggling to figure out what the exact balance is. This weekend, I learned that I’m not the only one with this problem, and there are at least two design patterns that attempt to solve this.

The Problem with Complete Separation

Let’s use the Twitter app as a simple example:

twitter stream

In the Twitter case, you would have a Tweet Model which would have some of the following properties:

  • user (this would point to a User model, which would have the name, screen name, and image of the user who originally created the tweet)
  • text
  • number of retweets
  • number of favorites
  • created at time

Following the strict MVC patter, the cell should be completely unaware of the model. The TweetTableViewCell should actually be called something more abstract (since the Tweet refers to the model) – so it’s something like TextWithImageTableViewCell. The cell would have the following IBOutlets:

  • imageView – for the user’s image
  • textLabel – for the tweet’s text content
  • actionView – to keep track of the reply, retweet, favorite, actions

To set up all these outlets, the cell would have a configure method to be called by the ViewController similar to this:

- (void)configureWithImage:(UIImage *)image
                      text:(NSString *)text
              actionCounts:(NSDictionary *)actionCounts
{
        self.imageView.image = image;
        self.textLabel.text = text;
        [self.actionView configureWithActionCounts:actionCounts];
}

Pros

This pattern is great for re-using the TextWithImageTableViewCell. Now, if Twitter wants to use this same cell to in the Direct Messages Table View, it is completely independent of the model, so it takes very little work to make this happen. This is the power of the MVC pattern.

Cons

The problem with this pattern becomes apparent when your product manager comes by and says something like “Now we need to also display images if there are any”:

Twitter App Image Cell

You now have to go back to the cell’s configure method and add another argument:

- (void)configureWithImage:(UIImage *)image
                      text:(NSString *)text
              actionCounts:(NSDictionary *)actionCounts
              anotherImage:(UIImage *)anotherImage
{
        self.imageView.image = image;
        self.textLabel.text = text;
        [self.actionView configureWithActionCounts:actionCounts];
        if(anotherImage) {
           self.anotherImageView.image = anotherImage;
        }
}

Now you have to go back to each viewController that uses this configure method, and add that extra argument.

This might not seem like a big deal for just one extra argument, but there are a lot more arguments in most cases, and a lot more possible arguments that a product manager could decide to include in the new re-design in the future. This is just not flexible enough.

Another way to deal with having a lot of changing arguments is to instead have a configureWithData: method that takes in a dictionary of data:

- (void)configureWithData:(NSDictionary *)data
{
        self.imageView.image = data[@"image"];
        self.textLabel.text = data[@&"text"];
        [self.actionView configureWithActionCounts:data[@"actionCounts"]];
        if(data[@"anotherImage"]) {
           self.anotherImageView.image = data[@"anotherImage"];
        }
}

However, with this scenario, there is no real type checking on what the arguments in the dictionary are, so this will get pretty messy – you configure method will have to check if each argument exists and that it is the correct type.

- (void)configureWithData:(NSDictionary *)data
{
        UIImage *image = data[@"image"];
        if(image && [image isKindOfClass:[UIImage class]]) {
           self.imageView.image = image;
        }
        // do the same check for every other property
}

In both cases, the ViewController will have to do the work of separating out each argument, making the method very bulky, long, and harder to work with.

The only certainty in software development is that things are going to change! We need to use a much more flexible pattern for Views and Models to accommodate these future changes.

Model-Driven Views

At CocoaConf San Jose this weekend, Mattt Thompson put a name to a pattern I’ve used before, but was not quite comfortable with – Model-Driven Views. Now that there’s a name for it, I feel a lot better about using it 🙂

In case of Twitter, you would have a TweetTableViewCell with an external tweet model property. When the view controller sets the tweet object, you can override the setter to configure the view:

// TweetTableViewCell.h

@class Tweet;

@interface

@property (strong, nonatomic) Tweet *tweet;

@end
// TweetTableViewCell.m

- (void)setTweet:(Tweet *)tweet
{
    _tweet = tweet;
    self.imageView.image = tweet.user.image;
    self.textLabel.text = tweet.text;
    [self.actionView configureWithActions:tweet.actions];
}

I used this pattern a lot when I first started working with iOS because it’s actually used very commonly in Rails, where the controller makes the model accessible to the view.

Pros

When the product manager comes over and tells you to add an additional image, all you have to change is this setter:

// TweetTableViewCell.m

- (void)setTweet:(Tweet *)tweet
{
    _tweet = tweet;
    self.imageView.image = tweet.user.image;
    self.textLabel.text = tweet.text;
    [self.actionView configureWithActions:tweet.actions];
    self.anotherImage.image = tweet.anotherImage;
}

Cons

Of course, the problem is that this view is so tied to the Tweet model, it is not very re-usable. If Twitter wants to use this same cell for their direct messages table view, the view would have to handle the Message model in addition to the of the Tweet model, which could cause some messy problems with having to check which model is the one set.

The ViewModel

The Paper team at Facebook gave a great talk a few weeks ago about some of the patterns they used to make Paper. One of those patterns was the ViewModel discussed by Jason Prado.

The designers at Facebook decided that a link should be displayed in the same way as a photo:

Facebook Photo ViewModel

The PhotoViewModel, would be instantiated with either the Photo or the LinkPreview Model, and will process the information so that the PhotoView can use the PhotoViewModel:

View Models

Pros

The ViewModel pattern solves the problems from the other patterns really well. The View is still abstracted and generic and can be easily re-used, but at the same time, it is powered by the ViewModel, which processes the model information so the view controller doesn’t have to pass the information to the view in piece-by-piece.

Cons

I haven’t tried this pattern before, but the only problem I see with it is the annoyance of creating ViewModels for everything. There are definitely some cases where a view will only be used only with a specific model.

For example, Mattt Thompson brought up an example of a BeerView – a view with a drawing of a beer glass that changes color and beer metrics based on which beer is represented, in which case using the ViewModel pattern seems overkill. I can see that pain paying off in a big way though in most cases, so I’m looking forward to using it.

How do you work with Models and Views? I’d love to learn about additional patterns in the comments.

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • This is an awesome write up 😀

    One major advantage of using model objects that I’ve run into personally is that they can also be reusable between different views. Say you’ve just built an iPhone app using table views and now you need to build an iPad app using collection views. With the model/view architecture it’s a trivial matter of reusing the model objects with your new collection view cells.

  • edsancha

    I’ve been using the model-driven view a lot without naming it too. Great post 🙂

  • Nicholas Gabriel Levin

    If you haven’t seen it before, Graham Lee’s series of posts chronicling the use (and misuse) of MVC is excellently done;

    http://www.sicpers.info/2014/01/meaningless-vapid-catchphrase/
    http://www.sicpers.info/2014/02/moderately-valuable-cliche/
    http://www.sicpers.info/2014/02/missing-vital-content/
    http://www.sicpers.info/2014/02/messily-veneered-c/

    And then he did a conclusion, of sorts, regarding how a solution on how to use MVC wisely;

    http://www.sicpers.info/2014/03/inside-out-apps/

    (Full disclosure; I contributed to the above series at one point. You’ll know when it you see it.)

    From my own experience, design patterns should be considered as guidelines, not religion, and few applications really do fit the whole models-or-views-and-whatever metaphor perfectly.

    Rather, I think about what MVC tries to solve, which is to make things modular and reusable. Then I think instead about what parts of my code could be broken up into little pieces, how those can be made as small as possible, and work from there.

    I can’t think of the last project where I had explicit “models” or “controllers”. There’s data objects, there’s remote data sources (models?), and there’s means of intecepting input (the old PARC definition of a Controller). Then there’s View Controllers in the land of iOS. Though, even in iOS, UIView objects are capable of handling user input, so they aren’t strictly “views” in the old PARC sense, either.

    Having patterns in your code that people can recognize is a good thing, of course. Just don’t worry about forcing everything into the same set of boxes.