iOS Development and the Wrong Kind of MVC
When starting out as an iOS Developer, you get to hear a lot about the MVC (Model-View-Controller) Pattern and how it’s the best when developing mobile applications. The MVC Pattern is a good way to think about certain problems when it comes to developing applications with a graphical user interface. It provides you with a way of thinking that lets you split your code into manageable packages, where each package will handle its own problem domain.
What are these packages you’re talking about?
There are three different conceptual packages in MVC that are supposed to handle problems like so:
The Model package handles what developers call The Business Logic. What The Business Logic is, depends on your particular application, but you can loosely generalize it to mean “the stuff in your application that manages your data and keeps it up to date and accurate”. This means that it’s the Models job to keep track of the position of your game characters, to know how many times you’ve played a song on iTunes, to save your edited photos to a format that you can share on social media… The Model is what makes your application smart.
The View in an MVC application will represent data that is sent from the Model and display it to the user. A View is supposed to be “dumb”, meaning that it should only know how to draw or display the data that it receives but should never make any adjustments to it or even be aware of what kind of data it is.
The Controller package is what makes an app fun to use. It’s responsible for interpreting the taps, presses, clicks, and tilts of your device, and send them off to make updates to the Model or the View. The Controller should be “thin”, meaning that it should just interpret the input and pass the appropriate signal along to the right place, in order for the application to make adjustments according to what the user expects. When information flows between the Model and the View, the Controller will act as a link.
So, what about iOS and “the wrong kind of MVC”?
When developing for iOS, the Model-View-Controller Pattern will often take the form of a MassiveViewController Pattern. Take a look at this example of a Controller class.
I’m sure this looks familiar and that you may have written something like this, I know I have. So what is the problem here?
The problem with this file is that it does not separate concerns. It is not adopting the MVC Pattern, and therefore the class gets massive and will be very hard to maintain if we make changes to the application in the future.
Looking at the code, you can see that this is supposed to be a Controller class, yet it takes on responsibilities of the View as well, setting up visual elements that are only there to display data to the user.
How do we improve it?
Above is the same code, but split up into chunks that correspond to a single MVC responsibility. Note that the Controller class shrunk a fair bit. However, the View class is almost the same size as the original file was. So what did we really win? This is the tricky part about MVC, because if you need to write more code, it may not always seem like you’re getting something out of it.
By refactoring the code like this, it will adhere to the Separation of Concerns Principle. The Controller class only deals with Controller problems and is, apart from initializing the corresponding View object, completely separated from everything that is related to displaying stuff to the user. This will be a valuable feature as the application grows, because one day you may want to replace a Controller class with a new, better one. If the Controller is separated from the View, that means less work for you and the likelihood of bugs and errors decrease. Also note that we defined two methods in our View to aid Controllers who may want to listen to tap events on either of the “interesting” parts of the view, as not to violate Law of Demeter.
Is that it?
Well, if we move away from the pure MVC aspect of it, you can see that our Controller class is depending very heavily on a single implementation of a View class. This goes against the principles of extensibility and coupling, so let’s try to fix that with some generics and abstractions.
Now we’ve got 4 files all of a sudden (7 files, if you separate the protocols into their own files). Is this really necessary??
What happened here is that we abstracted away any possible common behavior of both Controller and View classes, and put them into a Base class. The base class is not really meant to be initialized on its own, but defines and provides common operations and flows that every subclass will share.
This means that all future Controller and View subclasses will be smaller, easier to read, and easier to maintain as you won’t have to duplicate all the shared behavior. You will thank the stars if you decide to, for example, change the background image of all views. Instead of changing the same line in 20 classes, you will only have to change it in the one Base class. Easy!
This also means that if you want to change what kind of View class is associated with a certain Controller, it will be much less work. Since we adopted the MVCViewActionDelegate protocol, switching out the View class will be as easy as changing the class name in the generic constraint, and implement the new action delegate methods (if there are any accompanying the new View).
Hopefully, the way to think about MVC when developing your iOS applications has become a little less daunting and a little more clear after reading this. Feel free to comment if you have questions, and follow to get notifications about future articles.