Let’s discuss MVVM for iOS


Some say that as you progress as a software developer you undergo specific stages -

First, you’re glad you’ve made contribution to the code. You go around telling people that you created that glossy red button but you’re too shy to show your own code. You’re also happy that your code works and doesn’t cause fire accidents.

My head is on fire!!

Then, you start writing code that you can actually read yourself a few weeks later.

And then, you write neater code, share them online, and work with other developers.

And finally, you want write beautiful code. Code that reminds you of birds chirping in a lush green valley, clear blue sky, more like poetry. In short, a code that is clean, testable, reusable etc.

What about me? I’m still in the second stage, but hey!..a man can dream!

So recently, I’ve been obsessed with writing simpler code. My first target was the ViewController. If you’ve developed apps for iOS, you’ll know that ViewControllers are easily the most bloated file in the project. It handles a lot more code than necessary — ViewLogic, Model Logic, View Layout, WebService, Delegates etc., the list goes on and this is too much already.

If you dig a little deeper, you’ll find that there is something wrong with the design pattern. MVC. Massive View Controller some call it. It’s a design pattern and a standard approach to build iOS apps. At its heart, it’s a very simple design and a good idea. Here is the design -

In this 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.

MVC almost just works, however, as apps get larger, it is easy to wander off MVC, the controller gets bloated and it eventually becomes harder to test.

MVVM to the rescue

Lets look at this alternative pattern, MVVM. It stands for Model-View-ViewModel and it comes from Microsoft. It is quite similar to MVC, formalizes the tightly coupled nature of the View and Controller and introduces a new component.

Wait-.. isn’t this MVC but reworded? Nope. There is a difference. The views still don’t hold references to Model, but neither do Controllers. Instead, there is a new component called ViewModel.

The ViewModel encapsulates data/properties that the view can bind to and any validation logic and actions that can be performed. Typically, the ViewModel interacts with the model by invoking methods in the model classes. The ViewModel then provides data from the model in a form that the view can easily use. In this pattern we are essentially taking the state of our app and putting it into a ViewModel.

For the ViewModel to participate in two-way data binding with View, its properties must raise a PropertyDidChange notification, so that the View gets notified and can change its objects accordingly.

Benefits of MVVM

  • The ViewModel is an excellent place to put your validation logic for user input, view presentation logic, network requests. As a result, the ViewController files become far less bloated.
  • Also, the View-ViewController component is completely isolated from the model. So during development, one developer can work on the UI part of the screen and another can take care of the logic for the screen, independently and concurrently.
  • It is easy to redesign the UI without messing with the model logic since they are both completely isolated. As long as you link appropriate properties to the ViewModel, you can swap view components in and out. This gives more freedom to experiment with the UI.
  • For Universal apps, both iPad and iPhone ViewControllers can interact with the same ViewModel.
  • It’s easier to test. Developers can create unit tests for ViewModel and model without the View.

Enough talk. Let’s get our hands dirty

Let’s see how we implement MVVM in code. We’re going to build a simple iOS app that fetches photos from Flickr.

Here’s the PhotosViewController

@interface RSPhotosViewController ()

@property (nonatomic, strong) RSPhotosViewModel *viewModel;

@property (weak, nonatomic) IBOutlet UICollectionView *photosCollectionView;

@end

Notice that the ViewController owns the ViewModel instance. The ViewModel is nothing but a subclass of NSObject. It has only one photosArray property. Here’s how its .h file looks like.

@interface RSPhotosViewModel : NSObject

@property (nonatomic, strong) NSMutableArray *photosArray;

- (void)updatePhotos;

@end

The ViewModel.m file will handle fetching the images from Flickr.

To bind the properties of the ViewModel to the ViewController, I’m going use KVO (Key-Value Observing).

But if you’ve ever used KVO, chances are that you’ve run into various kinds of issues. The API isn’t great and forgetting to remove an observer may result in memory leaks or—even worse—crashes. Thankfully, Facebook’s KVOController library solves those annoying issues with KVO.

So let’s set up the observers. The ViewController is going to observe changes to the photosArray property. Any changes to it, will reload the photosCollectionView.

- (void)setupObservers
{
[_KVOController observe:self.viewModel keyPath:@"photosArray" options:NSKeyValueObservingOptionNew block:^(id observer, id object, NSDictionary *change) {
[self.photosCollectionView reloadData];
}];

}

If you run the app now, you’ll notice that KVO still doesn’t trigger. The code [photosArray addObjectsFromArray:photos] won’t fire KVO yet. You have to use proxy for collections — mutableArrayValueForKey: .

From the NSKeyValueCoding Protocol Reference

mutableArrayValueForKey:
Returns a mutable array proxy that provides read-write access to an ordered to-many relationship specified by a given key.

So your code should look like this,

[self fetchImagesCompletionBlock:^(NSArray *photos) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.photosArray removeAllObjects];
[[self mutableArrayValueForKeyPath:@"photosArray"] addObjectsFromArray:photos];

});
} failureBlock:^(NSError *error) {
NSLog(@"ERROR : %@", error);
}];

That’s it. Run your app, KVO will fire and voila! that’s MVVM for you.

And so…

Although there might be a slight increase in the amount of code, the complexity has decreased significantly. Even if the amount of code increases a little, there are way too many benefits in this pattern — A worthwhile tradeoff.

You can grab the complete code here.