Imagine you’re building a house. After months of waiting you sit down with the architect to see the plans. Unfortunately there’s a problem. Everything you asked for is there but it’s all in one giant room!
“I can’t take a poo in front of my spouse. It’s a line we don’t cross.” – you remark.
Just as designing a building requires dedicated rooms for dedicated purposes so does code. Model-View-Controller is a way to architect code into 3 distinct layers that achieve 3 separate purposes. Along with compartmentalizing code MVC also lays out a structure for how these layers should communicate with each other.
The nature of Apple’s UIKit framework makes it easy to separate objects in the view layer from the controller layer. However, because it’s not strictly enforced, there’s a tendency to put app logic and data in the vcs (view controllers), leaving them looking “double stuffed”.
yes but how could that be explained graphically using high sugar snack food?
The Model
Objects in the model layer represent the brain of your app. All of the data and logic associated with processing that data should be put in the model layer. Examples include:
- Calculations about sprite positions + physics related methods in a videogame
- Code related to network requests (often this gets placed in the controller object. Ideally it should go in a separate class file).
- Calculations regarding time, speed, distance, calories etc… in a running app
- Strictly speaking a dedicated object should be made for UITableViewDataSource, as opposed to including those delegate methods in the view controller.
Structs are an ideal data type for storing app data in the model layer. For example an array of image URLs could be stored in a separate swift file like so:
import Foundation struct ImageURL { // static constant with array of dictionaries static let imageArray = [ "Oreo" : "https://upload.wikimedia.org/wikipedia/commons/3/3e/Oreo-Two-Cookies.jpg", "Millano" : "http://betterbatter.org/wp-content/uploads/2014/02/milano-cookies-pinterest.jpg", "Pims" : "http://www.cooksinfo.com/edible.nsf/images/pims-biscuits/$file/pims-biscuits.jpg" ] // function to return url using image name static func CookieImageNamed(imageName: String?) -> NSURL? { if let urlString = imageArray[imageName ?? ""] { return NSURL(string: urlString) } else { return nil } } }
If in the future the URLs change there’s one obvious place to go to in the app to make updates. This is especially useful if such data is used by multiple view controllers.
Model layer objects should only communicate with other model objects and controller objects. If you’ve got UILabel, UIButton, UI(anything) in the model layer something’s gone wrong. Run quickly to the nearest box of Oreos – devour it – then come back on a MASSIVE sugar high and refactor that code!!!
The View
The view layer represents objects used in the user interface. In UIKit some examples include: UILabel, UIImageView, UIButton. Be aware that these are all separate classes (in the standard library), even if you don’t see them as separate files in your project navigator. All of the properties and methods that make up a UILabel are abstracted away inside its own class file.
Views receive information about how they should appear to the user via their associated controller object. Views also receive actions from the user (touch, swipe etc..) and pass this on to the controller. For example, a UIButton might have an @IBAction func in the view controller.
The Controller
In the center of our Oreo we have the controller layer. It’s going to sit in-between the view and model and will interpret how data in the model should be displayed to the user.
An important thing to realize is that every UIViewController already contains a view: viewController.view (i.e. a parent view for all other views). Even though there might be a visual representation of the UIViewController (or its subclasses) in interface builder the view controller itself is not considered part of the “view layer”.
Note: Every view must have a controller associated with it. Hence you can’t drag out UILables or UIViews directly onto blank space in the storyboard. They must be dragged onto a view contained within a UIViewController.
MVC Interpretations
One of the trickier aspects of MVC can be understanding how these layers communicate with each other. To add to the confusion there are slightly different interpretations of this in the programming world. Some design pattern books have objects in the view layer register themselves as observers of the model layer and only use the controller layer to process user input from the view layer and pass those state changes to the model.
If you’re interested in seeing what this architecture looks like I programmed a videogame, called Existence, in Java. GitHub link here. In this design JavaFX is used for the view layer.
The more widely accepted interpretation of MVC (the one endorsed by Apple and my Oreo diagram), is where the view and the model have no knowledge of each other and communication goes through the controller layer.
Conclusion
Following the guidelines of MVC (or similar modular architecture, e.g. MVVM) leads to more reusable code that’s easier to update. The modularily of MVC allows for easier testing and debugging. Once all the logic for your app is encapsulated in the model class it’s easier to write tests for it knowing that that code has nothing to do with the user interface, or view controller life-cycle etc..
Within the Cocoa environment the boundary between views and controllers is fairly easy to stick to. A lot of the anxiety of creating “massive-view-controllers” (aka double stuffed TM2016) can be alleviate by asking yourself “what is the data model that’s driving this controller?” whenever you create a new vc.
If you’ve found the post helpful please share with the links below!
For the latest updates + blog posts follow me on twitter here.
p.s. while researching MVC I noticed there are now “Mega stuffed” Oreos! WHY!???