Thursday, March 07, 2013

Update: This article has been updated to reflect the changes in the latest stable ReactiveCocoa v2

MVC - One Pattern to Rule them all

The MVC design pattern has existed since the late 1970s and has been in use in the Foundation, AppKit & UIKit Frameworks for a long time now. At its heart is a very simple design and a good idea. The Design is this

As you can see we have 3 components

Model - The Data model our view is managing

View - The User Interface on screen consisting of things such as text fields, buttons, etc.

Controller - The Controller in MVC exists to decouple the view from the model.

In the MVC design pattern, the controller separates the model from the view so that each can change independently. If we didn't have such a decoupling then every time the model changed, the view would need to change as well. In this design pattern interactions happen like this

A view triggers an action to happen on the Controller

The Controller executes said action and updates the model

The controller may receive a notification from the model that it was updated.

The Controller updates the view

This is the MVC waltz that we do when working with this design pattern, overall it works fairly well. The reason we use this is because this pattern decouples the view from the data model, letting the controller be an intermediary between the 2. This allows the controller to access the parts of the data model that need to be accessed and allows for the controller to do things like properly format the data for the view.

MVVM - For a Better World

MVVM stands for Model-View-ViewModel, and comes from Microsoft and is based off of the MVC design pattern. To be more precise, it is based off of Martin Fowlers Presentation Model. To some people at first it looks very similar to the MVC design pattern and people become confused as to how its different. So let me show you the pattern and then we'll see how its different

In this pattern we still have 3 components, the same as MVC however there are differences. In MVVM I and others tend to group the View Controller in with the View so that those 2 things are treated as 1 component. The most important part is that we've introduced the View Model and that the view gets updates pushed at it from the view model through some sort of observer. In this article ReactiveCocoa will be that observer.

In the MVVM pattern the View Model encapsulates data/properties that the view can bind to and any validation logic and actions that can be performed. For Instance if you had a button that needs to change its title text you would have a property on the view model that the button can bind its title property to. The same goes if you need to change the color of a control or enable and disable the control. In this pattern we are essentially taking the state of our app and putting it into a view model. Its also good to note that as far as the View Model is concerned it doesn't care where it gets this state from. It doesn't matter if it gets it from its init method, a file on disk, Core Data, a database, etc.

Because we are encapsulating the state of our view in such a way this has several benefits.

We don't need to test the UI

In terms of standard controls we don't need to test our UI's at all since the controls are just binding their state to the view model. Apple should be testing the controls that we are using, and you should be testing the custom controls you create. This means that as we bind our controls state to the view model we don't need to query the controls for their state as we should be able to access it within the view model itself. This feature tends to lending itself well to the next feature...

Because the UI is just binding to the view model, this means you should be able to swap components out and just bind the apppropriate properties to the view model and your UI should work the same as before. This should also make it easier for UI designers to experiment around with different views in your app.

ReactiveCocoa: An introduction

As I mentioned ReactiveCocoa is an implementation of Reactive Extensions .NET for Cocoa. The API is not an exact mapping of the API from Rx to Cocoa as explained in the project docs. ReactiveCocoa is also built using the concept of Functional Reactive Programming.

Functional Reactive Programming (FRP) is a programming paradigm for writing
software that reacts to change.

FRP is built on the abstraction of values over time. Rather than capturing
a value at a particular time, FRP provides signals that capture the past,
present, and future value. These signals can be reasoned about, chained,
composed, and reacted to.

By combining signals, software can be written declaratively, without the need
for code that continually observes and updates values. A text field can be
directly set to always show the current timestamp, for example, instead of using
additional code that watches the clock and updates the text field every second.

Signals can also represent asynchronous operations, much like futures and
promises. This greatly simplifies asynchronous software, including networking
code.

One of the major advantages of FRP is that it provides a single, unified
approach to dealing with different types of reactive, asynchronous behaviors.

Lets jump right into ReactiveCocoa through a couple examples and explain what is going on.

When this runs it'll give the output "Name changed to Hypnotoad" and then "Name changed to Nibbler". What we did was create a signal around a key path in self, and then subscribed to that signal and received events on the stream from that signal. Think of it this way, once we create a signal around a key path in this manner anytime the value of that key path changes the signal will see this and send a -next signal with the updated value. We automatically subscribed to this signal when we called -subscribeNext on it.

Signals only send 3 types of messages

next : This is how we get updated values from the stream

error : This is sent anytime the signal could not complete

completed : Sent to let you know that there will be no more signals on the stream

Although we could achieve this through other means, I wanted to create this manually. This method returns a signal which was created to get the string contents of a URL address and then return them to us. For this I used NSStrings built in method to retrieve a string from a URL. All we need to do is check to see if we have a result and if we don't then we can send -next and then -completed because we don't intend to send anymore messages.

Besides receiving updated values which we'll likely use for assignment to variables, signals also can be used in validation.

This particular example would work on iOS if you had a Username field, password field and you wanted to know if it was filled out correctly. For the purposes of this example we will only concern ourselves with making sure the username & password has a length greater than 0 and doesn't have any spaces in the user name. The combineLatest part of the method makes sure that we get the latest values from the textfield signal, then in the reduce section we are simply returning a number (0 or 1) to indicate if this is valid. When we hook this up to the enabled property of a control we get...

RAC(self.loginButton.enabled) = nameFieldValid;

and from there on out we get a button that is automatically enabled & disabled when the correct conditions arise from the signal we binded it to. Similarly we can bind controls to a view model like so...

RAC(self.textField.text) = RACObserve(self.viewModel,title);

What is going on here is that ReactiveCocoa is implicitly setting up all the necessary infastructure to bind a field to a signal. If you expand the RAC fields you get something like...

Be thankfull you don't have to write all that every time you wanted to bind properties in your code.

What is the ViewModel?

So i mentioned the View Model earlier as being the thing that our user interface binds its properties to as well as containing the actions that can be done to the view model and any validation logic. Lets show a simple example and get started figuring out how the VM in MVVM works. Here is a screenshot of the UI we'll work with...

A short note about @weakify(self); and @strongify(self);, this is one of the things you can do in addition to using __weak id bself = self; in your apps to avoid retaining self. This particular bit of code is was originally from libextobjc and then incorporated directly into ReactiveCocoa itself. In particular this has the same effect of creating a weak reference to self. I like this approach because you just keep using self.[property]... in your code and you don't have to constantly remember to create a bself variable. Check out EXTScope.h in libextobjc or RACEXTScope.h in ReactiveCocoa.

as you can see its relatively simple because we are only really concerned with a couple properties related to the player. The other properties are for controls to bind to and for some validation. We also have a single action method and a couple signals. The one thing you'll notice is the View Model has no clue what the UI it'll bind to is like at all. This is great because it means you have flexibility in what you bind things to and you can hook up to the view model in ways that make sense for the class thats binding to it.

Now lets look in our ViewController and how it is using these properties and signals...

__weak CDWViewController *bself = self;

To make sure we don't retain self in our blocks we need to use a weak reference. The ReactiveCocoa Project also has @weakify and @strongify for doing this in blocks, but i'll let you investigate that and see if you want to use it.

The first control is the UITextField for the player name, so we need to bind its text property to the view models playerName property. We also need to make sure we update the view model with updates from the text field. This ensures that the text field receives any updates from the view model and also makes sure that the view is updating the player name property on the view model.

//the score property is a double, RC gives us updates as NSNumber which we just call
//stringValue on and bind that to the scorefield text
RAC(self.scoreField.text) = [RACObserve(self.viewModel.points) map:^id(NSNumber *value) {
return [value stringValue];
}];

We need to bind the labels text property to the view models points property. The view models points property is a double, but ReactiveCocoa gives us updates to this as NSNumber's so we need to map this to a NSString which the label can use.

Simple binding and setup. Give the stepper the initial value from the view model and bind the properties (stepValue,min,max) to the view model.

//bind the hidden field to a signal keeping track if
//we've updated less than a certain number times as the view model specifies
RAC(self.scoreStepper.hidden) = [RACObserve(self,scoreUpdates) map:^id(NSNumber *x) {
return @(x.intValue >= bself.viewModel.maxPointUpdates);
}];
//only take the maxPointUpdates number of score updates
[[RACObserve(self.scoreStepper,value) take:self.viewModel.maxPointUpdates] subscribeNext:^(id newPoints) {
bself.viewModel.points = [newPoints doubleValue];
bself.scoreUpdates++;
}];

We'll enforce a rule from the view model that at any given time you can only update the points 10 times. To do this I created a NSUInteger property (in fact the only property we ever actually add to the view controller.) Then bind the hidden property to a signal which reduces to a 0 or 1 based on if the number of times we've updated is greater or equal to the number of times we've updated the points. So as soon as it hits the number we want this will return 1 and flip the hidden property to YES. To update the points we subscribe to the value property of the stepper and anytime it updates we update the view model and increment the player points.

If you remember the view model has a validation signal which indicates if any names given are forbidden. My list is really bad and not representative of what I personally think are bad words by any stretch of the imagination. The beauty of this is that we don't need to know any of the details of what the view model thinks is a "bad word", but it can just provide a signal to indicate that it thinks something should be forbidden. The view model can provide signals for many things like this that it thinks are important to let you know about it. In this case all we've decided to do is show an alert view and reset the player name.

//let the upload(save) button only be enabled when the view model says its valid
RAC(self.uploadButton.enabled) = self.viewModel.modelIsValidSignal;

Another signal that the view model provides to us is a signal letting us know if the view model as it exists it valid or not. In this case i've hooked this signal to the enabled property of the upload button. So we should not be able to upload the player stats if the state of the view model is bad. The signal here is simple

So for the purposes of this simple demo, we are just concerned if the name length is greater than 0, the name isn't in the forbidden names and the points are greater than the minimum. Really we should be concerned with more, but I won't extend it further. We could even just put the forbidden names signal in if we changed it to use map and return a NSNumber with 0 or 1 and so we could use it here inside this signal.

//set the control action for our button to be the ViewModels action method
[self.uploadButton addTarget:self.viewModel
action:@selector(uploadData:)
forControlEvents:UIControlEventTouchUpInside];

The button is set to trigger the View Mode's upload data action. This method in the sample code simulates uploading something then presents an alert view letting you know about the success of uploading.

Here since there is already signals enabling the button now the number of uploads need to be limited. This is subscribing to the signal for the UIControlEventTouchUpInside event and on the nth update where n is kMaxUploads. On the nth update the namefield is disabled, and the score stepper and upload buttons are hidden.

All of this results in a UI that just binds its properties to a view model, sets its actions to actions on the view model and receives signals from the view model indicating things of interest. I admit this particular example is a bit weird, but it demonstrates using a view model and using the basic aspects of ReactiveCocoa. There is a lot more to ReactiveCocoa in particular that I haven't shown here for the sake of not introducing too many concepts all at once. Additionally we could build some better view models for dealing with networking code and could deal with child view controllers, but that will have to wait for another article.

So what have we seen here?

MVVM stands for Model-View-ViewModel

The View model contains things like properties to bind to, validation logic and actions to be done to the view model

The View Model should not know anything about the UI that will bind to it

ReactiveCocoa is an implementation of many of the API's in Microsofts Reactive Extensions for .NET in Cocoa

We learned how to bind & subscribe to properties

We learned how to create signals

The biggest thing you can see with reactive cocoa is that all the code and signals are just reacting to events. When we chain these responses together and filter events properly we heavily reduce the need to create tons of variables and methods just to keep track of the events going on. This leads to more reliable code and with the view model we can easily test it and simulate a UI without actually having a live UI in our app.

18 comments:

Thank you for this introduction to MVVM with ReactiveCocoa.I am not familiar with this pattern and I wonder where would stand the model in your sample. Would the View Model update the model? In that case, I guess we could use ReactiveCocoa to update it when valid values are considered valid?

Philou: If I understand you correctly the viewmodel itself can contain in it a model class instance. For example we could have a Player class and instantiate it inside the view model and just have the view model update it and or update its properties directly from the view controller. The view model as a whole is just a thing we can bind the UI to. So as long as your data model class and other UI state are contained within it thats just fine.

Either of these would work just fineRAC(self.label.text) = RACAble(self.viewModel.playername);

Appreciate the discussion of FRP as it's a topic I'm interested in learning myself. However, your initial discussion of MVC and MVVM is rather messed up. What Microsoft call MVVM is pretty much what Apple call 'MVC'. When Microsoft discuss 'MVC', they're actually talking about Smalltalk-style MVC. And Smalltalk-style MVC != Apple-style MVC. (Yes, there really are two very different architectural styles with exactly the same name. Yayy, programmers!)

In Apple-style MVC, a clear distinction is made between the part of the View code that provides presentation display (i.e. draws buttons and boxes) and the part of the View code that provides presentation logic (e.g. makes sure that button A is disabled/enabled depending on whether box B has something typed in it). Input handling, meanwhile is handled in much the same way, with the presentation display part being responsible for receiving the initial mouse clicks and key presses, which are then passed down to the presentation logic part (or even skip over it) via delegation.

Apple call the presentation display part the 'View' and the presentation logic part the 'Controller'. This Controller layer essentially acts as an intermediate between their View and Model layers, performing actions on the Model in response to user input, and re-synching the View's content when the Model's own state changes.

Whereas... in Smalltalk-style MVC (which predates Apple's definition by several years), the View layer encapsulates both the presentation display and presentation logic. The Controller layer, meanwhile, has absolutely nothing to do with the View. Instead, both the Controller and the View sit side by side atop the Model; the Controller dealing with the user input side (mouse clicks, key presses) and the View dealing with the user output side (displaying data on screen).

...

The other thing to realize is that Apple's own particular fine-grained division between visual display ('View') and its backing logic ('Controller'), while a useful distinction, originally arose more or less by accident. It's basically an artefact of the original MacOS developement toolkit, where the presentation display was constructed in a visual editor while the presentation logic was constructed in a text editor. And since NeXT arose from Apple, it's no surprise that they continued this arrangement in the form of IB and AppKit.

However, pretty much everyone else in the world (MS, Linux, etc.) continues to use the standard Smalltalk definition when talking about 'MVC', no doubt aided and abetted by their traditional habits of constructing presentation display in code along with the rest of the app. Which is why MS eventually invented the term MVVM (an even more confusing and poorly considered name!) when they decided that they would also like to split presentation display and logic into separate layers for more flexible development and maintenance.

Meanwhile, many years of lulz merrily ensue as nobody in the world can be entirely sure what all the letters after 'M' actually stand for any more... :/

One other thought just struck me... Given that Apple MVC already allows the View layer to be constructed using something other than ObjC, I wonder if the same couldn't be done for the Controller layer as well?

The more I think about it, the more I like the thought of using FRP for as much Controller logic as possible. (Apart from anything else, it'd allow the crippled mess that is Cocoa Bindings to finally be taken out back and shot.)

Given that ObjC is hardly the ideal language to be expressing functional concepts, I wonder if someone could invent a nice functional-style DSL (domain-specific language) specifically for writing Controller layers in Mac apps, one that could play nicely with both the IB-constructed View and the ObjC-constructed Model while itself being optimized for the fast and easy construction of the Controller glue in between? Mmmm....

"How do you suggest performing viewcontroller transitions using MVVM?"

Some stuff like that you might have to do in the controller. Because Cocoa is MVC it is impossible to use MVVM everywhere in it even if you wanted to. This is because popping view controllers requires knowledge about the UI layer which the view model isn't supposed to know about. You could react to a change in the view model in the controller and pop yourself there.

Depends, if its something specifically to do with UI layer then it should be kept out of the view model. The view model should not know anything about any class in UIKit or AppKit. If it is just asking for data (strings, numbers,etc) then there is nothing wrong with having that in the view model imo.

that may be an instance where you should probably still use MVC. Since Cocoa (Touch) is built around MVC there are situations where its more practical to use MVC than MVVM. Thats not to say you couldn't use MVVM here, but since UITableView is built on the concept of a delegate that it queries for attributes of the view it is going to draw, I'd work with how UITableView works than against it in this instance.

Anonymous: Yes technically this is, really this should be done in some way where the view model doesn't have this in. For this small example though I didn't want to complicate it so I chose to do it this way for now. In the future I should rewrite it to avoid this.

Regarding table view binding, I find it quite frustrating that you have to drop back to implementing the datasource / delegate yourself in the view controller. This adds boiler-plate code to what is otherwise a concise class!

To tackle this, I have created a little 'binding helper' class that allow you to bind an array property to a table view, using a nib as a cell template. You can use it as follows: