In this article, I’ll talk about the difference between view controller code that just gets things done and code that is manageable for a long time.

I’ll introduce some popular design patterns that extend MVC, and at the end, I’ll give you some simple code tips that, when utilized early and often, will keep your view controllers sane.

(I won’t talk about separating the controller from web services, the file system and other sources of data, because I’ve written about it in my book and on this blog and this is already a lot to digest.)

The Opposite of Good

A naive implementation of a view controller method looks something like this:

- (void)submitForm:(id)sender
{
    NSString *name = [[self nameTextField] name];

    if([name length] < 4) {
        UIAlertView *av = ...
        return;
    }

    [[self statusLabel] setText:@"Submitting..."];
    [[self submitButton] setHidden:YES];

    NSDictionary *payload = @{@"name" : name};
    NSData *bodyData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:nil];
    NSMutableURLRequest *req = ....

    [NSURLConnection sendAsynchronousRequest:req queue:nil
    completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        [[self submitButton] setHidden:NO];

        if(!connectionError) {
            [[self statusLabel] setText:@"Submitted."];
            [[self navigationController] popViewControllerAnimated:YES];
        } else {
            UIAlertView *av = ...
        }
    }
}

In this method, the code works, all of the correct SDK calls were made, and it isn’t officially breaking any of the rules of model-view-controller.

But this is bad code. It doesn’t matter that it was “just to get it working” or “it’s fine in just this one place.” Eventually, this is going to ruin someone’s weekend.

This code is bad because it couples a task directly to an input. It assumes sole ownership of views on the screen. It lets the view drive the model. It handles errors with glaring redundancy. It has too much knowledge of the web services. It focuses on implementation details of concepts, and not the concepts themselves. It is a long list of side effects and therefore it’s basically untestable.

This is the birth of a complex and unmanageably large controller.

Related: View Controller Containers – Part 1

Managing Complexity with Acronyms

MVC is a set of useful guidelines for organizing objects in an application. If adhered to, an application’s code can become much easier to manage.

It is often said that when working within the MVC architecture, every object must be either a model, view or controller. Like any summary, there is a lot of room for interpretation. And so, I don’t interpret the must in that sentence in the same way I would a must in an RFC specification. Instead, I view MVC as a baseline architectural pattern. There should certainly be model, view and controller objects. But like a molecule is made up of atoms, these objects – especially controllers – are also composed of different types of objects.

These atoms are where the acronyms come in. MVVM. MVCS. FRP. VIPER. MVP. KVO.

To get us on the same page, let’s first understand that these are all patterns, not ideologies. They are tools. They are perspectives. Subscribing to a particular ‘silver bullet ideology’ is dangerous, but learning the lessons from these patterns is very useful. My goal here isn’t to advocate a particular design pattern, but to have a respect for each of their approaches and think about their concepts at a higher level.

Stepping back, the purpose of the application is pretty simple: allow a user to view and manipulate data. Here is a simple diagram:

View Controller

That line that connects the user to data is an interface. In its most simple form, that interface is probably a command line tool. That kind of interface is hard to learn and unforgiving, especially for the average user. So we introduce a friendly user interface full of buttons, labels and other quasi-tangible things:

View Controller

And now, we’ve given the user an easier and more forgiving interface, but introduced a new problem to the programmer: the view and data are tied together awkwardly. That means, if the data changes, so too must the view, and vice versa. Introducing a layer in between the view and the data minimizes the impact of change, which as the saying goes, is inevitable. That’s where MVC comes in.

View Controller

This concept of introducing layers with distinct interfaces between entities isn’t rocket science. (Although, I would imagine it has a place in rocket science.) For example, I wear clothing so that a change in temperature doesn’t impact my body temperature severely. Children get that.

In programming terms, the rationale for this layering is due to the underlying problem of state. Anytime you add something to an application, you are increasing the number of states that application can be in. For example, if I add a search interface to a screen, I’ve effectively introduced two new states: searching and not searching. By separating code into layers, I (ideally) only have to deal with this change within the controller (and maybe the view). The more layers I add – to a certain point – the more insulated changes in state are, and the less code has to change.

The acronyms I listed earlier present techniques for layering an application so that its state is more manageable. Let’s look at a few of them.

KVO (Key-Value Observing)

An input (like the user sliding a slider) triggers code that creates an output (like updating a label’s text and a model object’s property). A naive controller handles this by writing an explicit input method that creates this output:

- (void)slide:(id)sender
{
    [[self object] setValue:someValue];
    [[self label] setText:@"That Value"];
}

Cocoa’s binding system, which utilizes the KVO pattern, shifts this particular responsibility into another layer within the controller:

View Controller

As a programmer, you declare the appropriate bindings and another object (or objects) manage the control of transforming inputs to outputs in the controller. Thus, the number of states you have to explicitly manage in your controller class is reduced.

A benefit to KVO is that you can write a lot less code and manage state in a more automated way. A drawback is that it is a pain in the ass to debug.

MVVM (Mode-View-ViewModel)

This pattern shims another layer in between the model and the view by introducing a ViewModel layer. For example, a task list application would have Task model objects and a view that shows the due date and title of a task. The ViewModel, in this case, would grab a task’s title and transform its due date into a formatted string. This ViewModel would have a method like this:

- (void)setSourceTask:(STKTask *)task
{
    // Read from the model (task) and store presentable data in ViewModel's properties
    [self setTitleText:[task title]];
    [self setDueDateString:[[self dateFormatter] stringFromData:[task dueDate]];
}

The controller would respond to actions by updating the model, informing the ViewModel of change, and then applying ViewModel to the view:

- (void)action:(id)sender
{
    ... update task model object ...

    [[self viewModel] setSourceTask:[self task]];

    [self bindViewModelToInterface];
}

- (void)bindViewModelToInterface
{
    [[self titleField] setText:[[self viewModel] titleText]];
    ....
}

In other words, the ViewModel is an intermediate output format that is derived from the model and applied to the view. All the transformations and validations of data that need to occur happen in the ViewModel. All model changes are passed through the ViewModel on their way to the view. So, the diagram looks a bit like this:

View Controller

A benefit to MVVM is that is is a lot easier to automate tests for the ViewModel than it is the View. A drawback is that the insulating layer of the ViewModel can sometimes become too dependent on both the model and the view.

Related: View Controller Containers – Part II

FRP (Functional Reactive Programming)

This pattern is perhaps the most difficult to understand because while many developers have plenty of OOP experience, functional programming is a less practiced art. And so, we need to do a little bit of background.

We’re all familiar with algebra: put some value in a function and get another value out. Your code is no different – that is, your code maps an input to an output. The term map isn’t a fancy concept; the equation f(x) = y is just mapping x to y. A NSDictionary is just mapping keys to values.

Mapping is a fundamental concept to functional programming.

Another fundamental concept you already understand: filtering. A filter takes a group of things and outputs a smaller group of things based on the rules of the filter. For example, if you had the numbers 1-10 and were to apply a filter that only accepted numbers greater than five, the output would be the numbers 6-10.

And a third fundamental concept of functional programming is folding. You know what a fold is because you’ve summed together a group of numbers before. If I add the numbers 1-3 together, I get 6. The inputs are the numbers 1-3, the output is the total of those numbers, and the folding function is the sum function. More generally, fold takes a group of inputs to produce a single output.

FRP, with respect to a UIViewController, treats user interaction (like tapping a button) as inputs. An input is attached to a chain of maps, filters, folds and other functions that produce outputs. Those outputs are basically view and model objects.

Consider a controller that allows you to search a list of people by entering text in a search field. The input here is when the text in that search field is updated. The output would be a label displaying the number of results. The chain of functions would perform a filter with the search text and a list of source people, fold the resulting list into a single number by counting each element, map that number to a human-readable string:

View Controller

At the end, the result of the search chain of functions, you would map its final result – “6 results” – to the label.

You’ve probably already done this before, just across a number of different methods in a more imperative, Objective-C way. FRP, again, is accomplishing the same thing, but it guides you towards defining that chain of functions as a discrete unit that describes “this is what a search is.”

The useful part about FRP is that, if done right, it is really easy to attach different inputs and outputs to the “this is what a search is” chain, without changing that chain. For example, this chain was originally developed to take text from a search field as the initial input to the chain to produce a string that described the number of results.

But what happens when you want to give the user a table view of ‘Recent Search Items’ to pick from? You’d create another chain of functions that would transform the input of selecting a row in a table view that outputs a search term string, and plug that back into the “this is what a search is” chain.

View Controller

Putting it Together

Now, my point here wasn’t to convince you to switch to ReactiveCocoa or use bindings. Rather, it was to introduce some of the useful conventions that exist out there to reduce complexity in the code. Typically, these approaches will yield fewer lines of code as controllers get more complex (but for simple controllers, there won’t be much difference).

View Controller

It is often said that shorter code is more maintainable. I couldn’t agree more. But, I want to point out, shorter code isn’t about compacting ‘if’ statements into ternary operators. That’s because code length is not measured in lines or characters, but by the amount of time it takes to understand.

Thus, these patterns are really making code easier to understand by layering it in intelligent ways. And these patterns are pretty specific about how they do that, but I want to point out there are some underlying themes within them that are quite easy to add to your list of habits:

  • Separate Inputs, Tasks and Outputs.

Instead of worrying about what the interface looks like first, worry about it does. Does it allow you to add items to a list? Write primitive methods on the controller that handles working with this list:

- (void)addPerson:(STKPerson *)p;
- (void)removePerson:(STKPerson *)p;

If an action triggers adding or removing a person, always rely on these methods instead of directly accessing the model. (Also, don’t let the action method directly update the view, more on that in a moment.)

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip
{
    STKPerson *p = [[self sourcePeople] objectAtIndex:[ip row]];

    [self addPerson:p];

    [self bindModelToView];
}

This is a lesson from FRP: all of the logic for adding a person is in addPerson:. All of the mapping data-to-views logic is in bindModelToView. Transform an input to fit into that existing chain of code instead of re-writing the task and output in the action.

  • Create Conceptual Methods instead of relying on implementation details.

Code I see often:

- (void)toggleSearchInterface:(id)sender
{
    if([[self searchBar] isHidden]) {
        [[self searchBar] setHidden:NO];
    } else {
        [[self searchBar] setHidden:YES];
    }
}

The concept here is whether the search interface is visible. The implementation of that concept is the search bar’s hidden flag. When that implementation changes, you’ve got changes to make in potentially many places. Instead, wrap the concept of search interface visibility into its own methods:

- (void)setSearchInterfaceVisible:(BOOL)visible
{
    [[self searchBar] setHidden:!visible];
}

- (BOOL)isSearchInterfaceVisible
{
    return ![[self searchBar] isHidden];
}

- (void)toggleSearchInterface:(id)sender
{
    [self setSearchInterfaceVisibile:![self isSearchInterfaceVisible]];
}

When your controller starts changing whether the search interface is visible from places other than toggleSearchInterface: and then changes what constitutes as ‘search interface visibility’, this technique will save you time and headaches.

  • Let the model drive the view (in one pass!)

A little lesson from MVVM: let the model drive the view, and do it in one concrete unit. The example in item 1 showed code that did this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip
{
    STKPerson *p = [[self sourcePeople] objectAtIndex:[ip row]];

    [self addPerson:p];

    [self bindModelToView];
}

Where -bindModelToView was responsible for updating all of the views on the screen after the controller knew that the model changed. So, bindModelToView would be something like:

- (void)bindModelToView
{
    [[self firstNameField] setText:[[self selectedPerson] firstName]];
    [[self lastNameField] setText:[[self selectedPerson] lastName]];

    [[self searchResultsLabel] setText:[NSString stringWithFormat:@"%d results",
                [[self filteredPeople] count]];

    ...
}

When controllers get more complex, a method like this (or an object that accomplishes the same task, like in the case of a more concrete MVVM implementation) will be able to make better decisions about the full state of the controller than an input method or a primitive view controller method. In other words, avoid updating the view like this:

- (void)setPerson:(STKPerson *)p
{
    _person = p;

    [[self firstNameField] setText:[p firstName]];
    [[self lastNameField] setText:[p lastName]];
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)ip
{
    STKPerson *p = [[self sourcePeople] objectAtIndex:[ip row]];

    [self addPerson:p];

    [[self firstNameField] setText:[p firstName]];
    [[self lastNameField] setText:[p lastName]];
}

Additionally, don’t let the primitive methods even call into the model-to-view mapping either:

// Avoid this as well
- (void)setPerson:(STKPerson *)p
{
    _person = p;

    [self bindModelToView];
}
Joe Conway

Founder at Stable Kernel

Leave a Reply

Your email address will not be published. Required fields are marked *