In 2007 Apple unveiled the first release of iOS, the mobile operating system deployed on Apple mobile devices. Since its introduction, iOS has implemented a Model-View-Controller (MVC) development design pattern. This design pattern is a common way to organize code so that it is reusable and more easily extensible, and is the default way Xcode (Apple's IDE for iOS developers) organizes code.

In addition to code reuse and extensibility, using an MVC design pattern generally allows strong decoupling between business logic and UI logic. Unfortunately, iOS uses a combined roles variation of MVC by coupling a View with a Controller class into what is called a "View Controller" - creating a sort of "M/VC" variation of MVC.

In this post, we are going to look closer at the existing iOS MVC pattern variation and offer an alternative, and perhaps better, pattern for iOS development.

Introducing MVVM

One issue facing iOS developers is how to deal with major iOS updates for existing projects. More specifically, how to implement UI/UX changes as iOS evolves. Because iOS uses a combined view-controller design, this task can require a greater level of effort than should be necessary. Here's why: because the view and controller are coupled, an iOS view-controller class will usually contain both UI logic and business logic. This means that changes in the way the view is presented (UI logic) will usually also require changes to business logic within the same view controller class.

Further, as view controller classes implement increasingly complex UI requirements, the amount of business-logic code also tends to grow within the same view controller class. This, is turn, typically results in large, unwieldy, and difficult-to-read view controller classes.

Wouldn't it be better to have thin, flexible, easy-to-read view controller classes in iOS?

The MVVM Design Pattern

The "Model-View ViewModel" design pattern, or "MVVM", is similar to the MVC as implemented in iOS, but provides better decoupling of the UI and business logic. This decoupling results in thin, flexible, and easy-to-read view controller classes in iOS.

MVVM also provides better encapsulation. Business logic and workflows are contained almost exclusively in the viewModel (referred to as the view manager in the example project). The view/view controllers concern themselves only with the UI and know little, if anything, about the business logic and work flow in the viewModel.

MVVM is built around three fundamental parts: data model, view/view-controller, and viewModel:

1) Data Model

Just like in the MVC design pattern, the MVVM data model is a class that declares properties for managing business data. For instance, a banking app would need to manage user account data like account balances, transaction history, etc. These data objects are declared in the model as class properties with appropriate getters and setters.

2) ViewModel

The viewModel is at the heart of the MVVM design pattern and provides the connection between the business logic and the view/view controller. The view (UI) responds to user input by passing input data (defined by the model) to the viewModel. In turn, the viewModel evaluates the input data and responds with an appropriate UI presentation according business logic workflow.

The viewModel then is the hub of activity in the MVVM design, acting as an intelligent traffic control center for the model, business logic, workflow, and view/view-controller.

3) View/View Controller

The view/view controller is the context (i.e. the view controller class) that presents user interface elements. As mentioned above, in iOS the view/view controller is usually coupled to business logic within a view controller class.

Conversely, in MVVM, the view/view controller contains little or no business logic and is primarily responding to the viewModel to configure and present UI elements (e.g. table views, buttons, etc.)

Example

Let's take a look at what this might look like in an Xcode project. Below you will find a link to the MVVM_Example Xcode project. In our example project, there are three main classes that correlate to the MVVM design pattern.

The ViewController class is a typical iOS view/view controller class. The MVVM_model class is our data model, and the MVVM_viewManager class is our viewModel (in my opinion, the term "viewModel" is somewhat confusing, so I've used "viewManager" instead).

The starting point in our example project is the initWithCoder method found in the ViewController implementation file (ViewController.m) This method is called after the app is launched and the main storyboard is instantiated, thus presenting our first ViewController. The ViewController object (self) is then passed to the viewModel in the MVVM_viewManager initWithRootViewController method.

The initWithCoder method in the ViewController.m class:

<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span> initWithCoder<span class="sy0">:</span><span class="br0">(</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSCoder_Class/"><span class="kw5">NSCoder</span></a> <span class="sy0">*</span><span class="br0">)</span>aDecoder <span class="br0">{</span>
self <span class="sy0">=</span> <span class="br0">[</span>super initWithCoder<span class="sy0">:</span>aDecoder<span class="br0">]</span>;
<span class="kw1">if</span> <span class="br0">(</span>self<span class="br0">)</span> <span class="br0">{</span>
_viewManager <span class="sy0">=</span> <span class="br0">[</span><span class="br0">[</span>MVVM_viewManager alloc<span class="br0">]</span> initWithRootViewController<span class="sy0">:</span>self<span class="br0">]</span>;
<span class="br0">}</span>
<span class="kw1">return</span> self;
<span class="br0">}</span>

The view manager's initWithRootViewController method does some important setup:

<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">id</span><span class="br0">)</span> initWithRootViewController<span class="sy0">:</span><span class="br0">(</span>ViewController<span class="sy0">*</span><span class="br0">)</span>rootViewController <span class="br0">{</span>
self <span class="sy0">=</span> <span class="br0">[</span>MVVM_viewManager new<span class="br0">]</span>;
<span class="kw1">if</span> <span class="br0">(</span>self<span class="br0">)</span> <span class="br0">{</span>
_rootViewController <span class="sy0">=</span> rootViewController;
_dataModel <span class="sy0">=</span> <span class="br0">[</span>MVVM_model new<span class="br0">]</span>;
<span class="br0">[</span>_rootViewController setViewManager<span class="sy0">:</span>self<span class="br0">]</span>;
<span class="br0">[</span>self setup_kRootViewController<span class="sy0">:</span>rootViewController<span class="br0">]</span>;
<span class="br0">}</span>
<span class="kw1">return</span> self;
<span class="br0">}</span>

Specifically, the passed in viewController object is assigned to a newly created viewModel's rootViewController property. Next, the model created and assigned to the viewModel's dataModel property. Finally, a setup method is called for the rootViewController and it is presented.

At this point the our root view controller is showing and the viewModel is ready to manage user interaction.

Handling Workflow in the MVVM_Example

In the example project, workflow is handled exclusively in the viewModel. For instance, when the user touches a button in a view controller, the handle_button_action in the viewModel is always called. This method is called in the viewModel for all button actions in all view controllers that are instantiated with the <span class="br0">[</span><span class="br0">[</span>ViewController alloc<span class="br0">]</span> initWithViewManager<span class="sy0">:</span><span class="br0">]</span> method. This approach moves the logic needed to handle a button action out of the view controller class and to the viewModel

Tapping the "Next" button in the root view (shown above) initiates the viewModel workflow in the ViewController object:

<span class="sy0">-</span> <span class="br0">(</span><span class="kw4">void</span><span class="br0">)</span> handleNextButton <span class="br0">{</span>
<span class="br0">[</span>_viewManager handle_button_action<span class="sy0">:</span>self forViewControllerType<span class="sy0">:</span>_myViewControllerType<span class="br0">]</span>;
<span class="br0">}</span>

The handle_button_action method then calls a series of viewModel methods that instantiate and present the next view controller.


This is the general pattern used to set up and present all view controllers from the viewModel.

Advantages of MVVM Design Pattern

With its strong decoupling of the UI and business logic, the MVVM design pattern provides some very useful benefits.

Thin view controllers that are easily debugged

In MVVM, view controllers are concerned only with the UI. MVVM UI encapsulation means finding and fixing UI issues is usually a straightforward process that no longer involves digging through business logic trying to locate the root cause of a particular bug.

Flexible business logic and work flows

Adding and modifying workflows (e.g. which view shows when) is done very quickly in the MVVM design pattern. MVVM UI/business logic decoupling means changes can usually be done with simple one or two line changes in the viewModel.

Code reuse

Most init, setup, and view presentation methods are implemented in the viewModel class. As such, each new view controller shares those methods, which means no redundant view controller code.

Practical Considerations

As we've seen, MVVM as a design pattern in iOS is useful and yields many benefits. However, as with any design, care must be taken to understand the limitations and the appropriate implementation in any given project or project feature. Complex project features with a small number of views may not realize the same benefits of MVVM that a larger feature with many repetitive views would. Each developer must think carefully about the best design pattern for any given project. Hopefully you will find MVVM a useful approach in your latest iOS project.

Happy coding :p