If you’ve been developing iOS applications for any length of time,
you’ve probably heard of Model-View-Controller, or MVC. It’s your
standard approach to building iOS apps. Lately, however, I’ve been
growing tired of some of MVC’s shortcomings. In this article, I’m going
to go over what MVC is, detail its weaknesses, and tell you about a new
way to structure your apps: Model-View-ViewModel.
Source code : https://github.com/sibahota059/MVVM-iOS
Source code : https://github.com/sibahota059/MVVM-iOS
Model-View-Controller
Model-View-Controller is the definitive paradigm within which to structure your code. Apple even says so. Under MVC, all objects are classified as either a model, a view, or a controller. Models hold data, views present an interactive interface to the user, and view controllers mediate the interaction between the model and the view.
In our diagram, the view notifies the controller of any
user interaction. The view controller then updates the model to reflect
the change of state. That model then (typically through
Key-Value-Observation) notifies any controllers of updates they need to
perform on their views. This mediation makes up a lot of the application
code written in iOS apps.
Model
objects are typically very, very simple. Often times, they’re Core Data
managed objects or, if you prefer to eschew Core Data, other popular
model layers.
According to Apple, models contain data and logic to manipulate that
data. In practice, models are often very thin and, for better or worse,
model logic gets shuffled into the controller.
Views (typically)
are either UIKit components or programmer-defined collections of UIKit
components. These are the pieces that go inside your .xib or Storyboard:
the visual and interactable components of an app. Buttons. Labels. You
get the idea. Views should never have direct references to models and
should only have references to controllers through IBAction events.
Business logic that doesn’t pertain to the view itself has no business
being there.
That leaves us with controllers. Controllers are where the
“glue code” of an app goes: the code that mediates all interactions
between models and views. Controllers are responsible for managing the
view hierarchy of the view they own. They respond to the view loading,
appearing, disappearing, and so on. They also tend to get laden down
with the model logic that we kept out of our model and the business
logic we kept out of our views. That leads us to our first problem with
MVC…
Massive View Controller
Because of the extraordinary amount of code that’s placed
in view controllers, they tend to become rather bloated. It’s not
unheard of in iOS to have view controllers that stretch to thousands and
thousands of lines of code. These bulging pieces of your app weigh it
down: massive view controllers are difficult to maintain (because of
their sheer size), contain dozens of properties that make their state
hard to manage, and conform to many protocols which mixes that protocol
response code with controller logic.
Massive view controllers are difficult to test, either
manually or with unit tests, because they have so many possible states.
Breaking your code up into smaller, more bite-sized pieces is typically a
very good thing.
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.
So where, then? MVC simply doesn’t have a place for code that doesn’t fit in within its three components.
Poor Testability
Another big problem with MVC is that it discourages
developers from writing unit tests. Since view controllers mix view
manipulation logic with business logic, separating out those components
for the sake of unit testing becomes a herculean task. A task that many
ignore in favour of… just not testing anything.
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?
You might have seen this joke on Twitter a while back:
“iOS Architecture, where MVC stands for Massive View Controller” via Colin Campbell
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.)
MVVM comes from Microsoft,
but don’t hold that against it. MVVM is very similar to MVC. It
formalizes the tightly coupled nature of the view and controller and
introduces a new component.
Under MVVM, the view and view controller become formally
connected; we treat them as one. Views still don’t have references to
the model, but neither do controllers. Instead, they reference the view
model.
The view model is an excellent place to put validation
logic for user input, presentation logic for the view, kick-offs of
network requests, and other miscellaneous code. The one thing that does
not belong in the view model is any reference to the view itself. The
logic in the view model should be just as applicable on iOS as it is on
OS X. (In other words, don’t #import UIKit.h in your view models and
you’ll be fine.)
Since presentation logic – like mapping a model value to a
formatted string – belong in the view model, view controllers themselves
become far, far less bloated. The best part is that when you’re
starting off using MVVM, you can place only a little bit of logic in
your view models, and migrate more of it over to them as you become more
comfortable with the paradigm.
iOS
apps written using MVVM are highly testable; since the
view model contains all the presentation logic and doesn’t reference the
view, it can be fully tested programmatically. The numerous hacks
involved in testing Core Data models notwithstanding, apps written using
MVVM can be fully unit tested.
The results of using MVVM, in my experience, is a slight
increase in the total amount of code, but an overall decrease in code
complexity. A worthwhile tradeoff.
If you look again at the MVVM diagram, you’ll notice that
I’ve used the ambiguous verbs “notify” and “update”, but haven’t
specified how to do that. You could use KVO, like with MVC, but that can
quickly become unmanageable. In practice, using ReactiveCocoa is a great way to glue all the moving pieces together.
To know more about it with an Example, here you will find the source code.
Coding difference between MVC & MVVM
Below screenshot is for OLD MVC pattern.
To know more about it with an Example, here you will find the source code.
Coding difference between MVC & MVVM
Below screenshot is for OLD MVC pattern.
Below screenshot for MVVM pattern (Compare viewDidload of both)
Below Screenshot for View model Class
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 :)