Good MVC design comes with a number of benefits, including improved code readability, testability, and extensibility. At GameChanger, we’ve seen our crash rates drop significantly after rewriting parts of our app to follow good MVC design principles. But what exactly is the proper way to implement MVC in Cocoa Touch?
Apple’s recommendations for MVC leave a lot to be desired for iOS developers. The recommendations come with little to no explanation and some of them pertain to concepts specific to OS X (e.g. mediating controllers and Cocoa bindings). To confuse matters further, the name “UIViewController” wrongly suggests that view logic and controller logic belong in the same class. I want to explain how we at GameChanger interpret Apple’s recommendations and how we’ve applied them to improve the quality of our codebase.
Below is a diagram depicting Cocoa’s version of MVC. In an ideal world, each box represents a distinct set of classes.
Below are Apple’s recommendations for MVC (in bold), with the parts irrelevant to Cocoa Touch removed, and with some additional translations.
1. “Although you can combine MVC roles in an object, the best overall strategy is to keep the separation between roles”
Translation:
a) The best strategy is to put only controller logic in a UIViewController and put the view logic in a separate file. The UIViewController subclass called XYZController can either load a nib from a file called XYZView.xib or initialize a UIView subclass called XYZView.
Example:
b) Controllers can configure views through properties and methods of view classes or through IBOutlets in nib files. Views communicate back to controllers through the target-action pattern or delegate pattern. It’s best to use target-action for simple views containing just a few UIButtons. It’s better to use the delegate pattern when the view needs to interpret more complex interactions, e.g. in a UITableView or a view with custom animations.
Target-Action Example:
Delegate Example:
c) Controllers should not be doing elaborate processing of data. It’s better to create a model class or a datasource class, or create a separate data processor class and post a notification when data has been updated.
2. “A goal of a well-designed MVC application should be to use as many objects as possible that are (theoretically, at least) reusable”
Translation:
a) Use as many views as possible - Views are generally very reusable so this makes the code base more extensible. UIViews follow the composition software pattern so it’s easy to build up a more complex view from simple reusable subviews. Reusing views also improves UI consistency.
b) Use as many model objects as possible - Models are also very reusable. It’s also easier to make more controllers when model data isn’t tied directly to a specific controller.
c) Controllers are generally less reusable, but in most cases it’s still better to have a few simple controllers than one large controller that does lots of different things. The Single Responsibility Principle is key here.
3. “A view class shouldn’t depend on a model class”
Translation: This one is pretty apparent from the MVC diagram.
4. “A model class shouldn’t depend on anything other than other model classes”
Translation: Model classes shouldn’t send messages directly to controllers. Controllers can learn about changes to models through NSNotificationCenter or Key-Value Observing.
5. “A [UIViewController] depends on classes of all MVC role types”
Translation: This one goes without saying.
Using good MVC design is important for maintaining a healthy codebase. Cocoa touch is a very flexible framework which is nice in some cases but that means that it’s even more important to have additional guidelines to keep your code quality in control. These guidelines don’t work in every case (UITableView probably warrants its own blog post) but they serve as a useful guideline during the software design process. A little bit of extra time spent thinking carefully about MVC during the design stage saves a ton of time and headaches later on.