So on the project I’m working on, an architect has asked us to use the MVVM model on iOS to develop certain components within the application. If those components work out correctly, then eventually we’ll be asked to refactor the rest of the application to use the same design pattern.
Okay, so here are some random thoughts about this.
First, I’m not a fan of “Design Patterns” as we currently seem to be using the term.
To me, a “design pattern” is essentially a technique for solving a problem.
And for those who think this is a difference without a difference, Design Patterns are not just useful ways to think about a problem in order to solve it, but actually represents specific codified solutions which are intended to be used relatively unmodified. Techniques, on the other hand, represent ways of thinking about a problem which may or may not be reproduced with perfect fidelity from place to place.
To give a concrete example of what I mean by this, take the current Model-View-Controller Design Pattern. As described on Wikipedia, it represents a Core Solution to building User Interfaces, where each component is well defined: a “view” which generates an output representation, showing data from a “model” which passively stores data the user manipulates using a “controller” which mediates messages between the two.
But if you go back to the original papers discussing Model-View-Controller, you see something much less rigid in thought: it was a way to separate the functionality used to drive a user interface into three loosely grouped ideas: views which show things, models which store things and controllers which manipulate things.
A technique, in other words, to help you organize your thoughts and your code better.
Second, not all techniques are “One Size Fits All.”
Take MVC again. There is nothing that requires your user interface application to use all the pieces of a model view controller: in fact, one could very easily write a simple calculator application that has no model at all.
For example, here is the UIViewController class of a trivial application which takes the input of a text field and converts from celsius to fahrenheit:
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *centigrade;
@property (weak, nonatomic) IBOutlet UITextField *fahrenheit;
double c = [self.centigrade.text doubleValue];
double f = 32 + (9/5) * c;
self.fahrenheit.text = [NSString stringWithFormat:@"%.2f",f];
Now the most pedantic jerk would say “well, technically the above is a perfect example the MVC Design Pattern, with your model implicit in the method doConvert:, in the variables c and f.”
To which I’d respond really??? Are you so hell bent to squeeze everything into an artificially strict interpretation that you must find a model where one doesn’t really exist?
And thus, the difference between “technique” and “Design Pattern.”
Third, there are far more techniques under the sun than the so-called “Gang Of Four” first espoused upon, techniques that we have forgotten are design patterns in their own right.
Remember: techniques are ways of thinking about a problem that help solve a problem, rather than strictly formed legos in the toy chest that must be assembled in a particular way.
So, for example, “separation of concerns” is a design pattern in its own right: a way to think about code that involves separating it into distinct separate components which are responsible for their own, more limited jobs.
Take the TCP/IP software stack, for example. The power of the stack comes from the fact that each layer in the protocol is responsible for a very limited job. But when assembled into a stack it creates a rather powerful communications paradigm that underlies the Internet today.
So, for example, the link layer is responsible for talking to the actual physical hardware. The IP layer is responsible for routing; in essence it is responsible for converting an IP address into the appropriate hardware device to talk to on the local network, and for receiving incoming IP packets addressed to this computer.
But the IP layer makes no guarantees the message is sent successfully; instead, that lies on the TCP layer, which chops large messages up into packets that fit into an IP frame, and which tracks which packets have been successfully sent and received, sending an acknowledgement when a packet is received successfully. This allows TCP to note when a packet goes missing and trigger a resend of that packet.
And on top of these three simple relatively straight forward components a global Internet was built.
The thing is about patterns like the Separation of Concerns is that because it’s so fundamental to the way we think about software development we forget that it is yet another technique, yet another design pattern that developers use. In fact, we’ve taken it so for granted we no longer really teach the concept in school. We just assume new developers will understand how to break their code into separate distinct modules, each reflecting a specific concern.
Other techniques we’ve simply dropped on the floor, forgetting their value.
For example, we’ve forgotten the power of finite state machines to represent computational state when performing a task. Yet the tool YACC rests on the work done on finite state machines, by converting a representation of a language into a state machine which can be used to parse that language. Similar state machine representations have been used when building parsers for ASN-1 communication protocols, are often used to represent the internal working of IP, and are implicit in the design of virtual machines, such as the JavaVM system.
But because there is no One True Way to implement a state machine, it’s seldom thought of as a Design Pattern, if it is even thought of at all.
Let’s go back to MVC for a moment.
The original idea behind Model View Controller was simply as a technique to think about how to organize your code into separate concerns: one which handles the data model, one which handles the views the user sees, and one which controls how views and models interact.
Think of that in the context of the following article: Model-View-ViewModel for iOS
Missing Network Logic
The definition of MVC – the one that Apple uses – states that all objects can be classified as either a model, a view, or a controller. All of ‘em. So where do you put network code? Where does the code to communicate with an API live?
You can try to be clever and put it in the model objects, but that can get tricky because network calls should be done asynchronously, so if a network request outlives the model that owns it, well, it gets complicated. You definitely should not put network code in the view, so that leaves… controllers. This is a bad idea, too, since it contributes to our Massive View Controller problem.
If you think of MVC as an organizational principle, the question “where should you put your network code?” becomes painfully obvious. It belongs in the model code.
But it also assumes the model code also may contain business logic which affects how objects within the model may be manipulated, as well as alternate representations of the data within the model.
But if you think of MVC in the way we’ve grown accustom to, then the “model” is a bunch of passive objects, no better than a file storage system. And if you think of the model code as a passive collection of data objects to be manipulated and perhaps serialized–then of course “where should you put your network code?” becomes a pressing concern.
Just as if you think of a kitchen as being a room that only contains a stove, refrigerator and a microwave, the question “where should I store my pots” becomes a pressing question.
“Well, in the cabinets.”
“But kitchens don’t have cabinets! They only have stoves, refrigerators and microwaves!”
Which is why, when you think about it, why MVC now seems to stand for “Massive View Controllers”–because if you don’t allow any logic in your view and you don’t allow any logic in your model, then you’re stuck slamming everything in the controller code, including shit that doesn’t belong there, like model business logic.
And into this world, we see MVVM.
After two or three ill-considered days of staring at this for the project I’ve come to some conclusions:
First, MVVM makes sense if you consider the responsibility of a controller to both handle the interactions of views within a view controller, and to handle the business logic for communicating with the model code.
Again, I believe this distinction is only necessary because we’ve come to think of views as stupid (and in an iOS application, generally tinker-toys we drag off the Xcode storyboard palette), and because we’ve come to think of models as stupid–at best serializable collections of Plain Ordinary Objects.
(Personally I like thinking of a model as being more than a pile of POO, but that’s just me.)
MVVM, as handled in environments like iOS, is really MVCVM.
In other words, you don’t get rid of view controllers. Instead, you separate the code so that view controllers handle the user interface (such as making sure table views are populated, that events repopulate changed controls, etc), and the “ViewModel” code handles the view-specific interaction logic with the model code.
Again, I believe the model code should be more than a pile of POO. But as an organizational principle it’s not a bad one, putting the code which manipulates the model separate from the code which manipulates the views, and having them communicate through a well described interface.
MVVM assumes a “binder”, or rather, a means by which changes in the ViewModel are sent to the View Controller/View combination.
So inherent in the design of MVCVM is the notion that changes made to a publicly exposed value in the ViewModel will be announced to the view controller so the views handled by the view controller will be automatically updated. Some documents describe using ReactiveCocoa, though one page suggested something as simple as a delegate could be used, though one could also use KVO to observe changes.
In some example code I’ve seen the View Controller instantiate the ViewModel object. In others, I’ve seen the ViewModel object passed to the initializer of the View Controller.
I gather that (a) there is supposed to be a “correct” (*ugh*) way to do this, but (b) if you want to use Storyboards or NIBs in order to create your view controllers, you’re sort of stuck with having the View Controller create the ViewModel. (Besides, being able to instantiate the ViewModel without the View Controller is supposed to allow us to supposedly test our user interface without having a user interface…)
On the other hand, you can always attach your ViewModel to the View Controller in the prepareForSegue:sender: method.
This feels like it’s solving a problem we shouldn’t have if we weren’t being such pedantic assholes.
Meaning if we hadn’t forgotten that MVC is an organizational principle rather than a strict formula, and hadn’t forgotten that our Views don’t need to be stupid and our Models doesn’t need to be a pile of POO, then we wouldn’t be left wondering where our network code belongs or wondering where to put our business logic.
Because the answer to that question would be immediately obvious.
But since this is where we are, separating out what properly belongs in the model code and calling it something new may help a new generation of developers realize they don’t need to build a big pile of sphagetti code to make their applications work.
This doesn’t guarantee better code.
The real problem, of course is that code will be no better than the programmer who writes it, no matter how many different techniques they try. A good, well organized programmer will produce good, well organized code. A poor, disorganized programmer will produce poor, disorganized code.