MVVM in iOS

A roller coaster in front of three big blocks.

One of my biggest concerns as a developer is how a project is structured. Defining a good and clean architecture with helpful names and nice design patterns is very important. It helps you and other developers to easily understand how different components of the code connect to each other and makes our lives easier in the long term. It’s also very important to keep a good documentation about it. Always keep the README.md file updated. It helps new developers a lot.

Gif of four megazords running.

Here at Cheesecake Labs we care a lot about how the projects are structured. This is very important when working in teams in the same codebase. This way everybody codes in the same style. Keeping code readable for everybody during the whole project.

Android Architecture Components brought a new way to connect the views to the backend of Android projects: the “ViewModel”. The name explains itself. It’s an intermediary layer that connects the View (UI) to the Model (Business logic, APIs and persistency). It makes the structure of Android projects more simple to understand and it is also a good way to keep view and business logic decoupled, organized and easy to test.

MVVM is not mentioned in Google’s documentation, but the implementation of the Android ViewModels fits very well in the MVVM design pattern. So that’s what we decided to use.

MVVM worked so well in Android projects that we decided to use it in iOS projects too. And the results have been very good so far.

MVVMC

ViewModel is not a new concept. It’s the main part of the MVVM design pattern that was first introduced in 2005 by Josh Gossman in this post at Microsoft’s blog. It was created to be used as a design pattern in .NET applications, but of course we can use it anywhere – including Android and iOS projects. Each platform has its implementation particularities, so for iOS we are using the way the Runtastic team introduced in this blog post. The MVVMC.

MVVMC is made of five main layers:

Image of MVVMC five main layers connected to each other.

View

This is the UI. It shows information to the user and tells the ViewModel about User actions. It should not know about ViewModel implementation, so the ViewModel can be unit tested with a “mocked” view. There are a lot of ways to connect these layers. Here is an example using simple delegates:

class RocketLaunchViewController: UIViewController {
     var viewModel: ArticlesListViewModel?   
     
     override func viewDidLoad() {
          super.viewDidLoad()
          viewModel?.onViewDidLoad()
     }
     
     @IBAction func didTapLaunchButton(_ sender: Any) {
          viewModel?.onLaunchButtonTapped()
     }
}
 
extension RocketLaunchViewController: RocketLaunchViewDelegate {
     func showSuccessMessage() {
          //Show success message
     }
}

Model

The model is an abstraction of the app data. Usually we define entities that represents the objects and business logic that define how these entities relate to themselves. Each project has a particular model structure that can be fetched from an API, persisted in the database, sent to a Bluetooth device, etc… Model layer is shared between all modules of the project, so it’s important to keep this layer consistent and safe.

ViewModel

This is the glue that makes all parts working together. The ViewModel is responsible for telling the View what should be shown to the user and to respond to whatever the user does in the UI. The ViewModel uses an Interactor to access the model layer and the Coordinator to communicate to other modules. Here is a small example:

protocol RocketLaunchViewDelegate: class {
     func showSuccessMessage()
}
 
class RocketLaunchViewModel {
     weak var view: RocketLaunchViewDelegate?
     var interactor: RocketLaunchInteractor?
     var coordinator: RocketLaunchCoordinator?

     func onViewDidLoad() {
          interactor?.fetchRockectData() { result in
               if result.status == .success {
                    self.view?.showSuccessMessage()
               }
          }
     }

     func onLaunchButtonTapped() {
          coordinator?.showLaunchProgressModule()
     }
}

Interactor

The interactor exposes the model layer to the ViewModel. Access to Database, API requests and other data that are external to the current module should be asked to the interactor by the ViewModel. Usually we create data access that is shared between all modules, like an API Manager, or Database Manager that concentrates logic that keep the data consistent. Interactor is the part that should communicate to these managers.

Coordinator

This part of the MVVMC pattern was added to handle communication between MVVMC modules. It is the way ViewModel uses to communicate and initiate other modules. This is also used to assemble a new MVVMC module. Code tells more than words, so:

class RocketLaunchCoordinator {
     var viewController: UIViewController?
     
     func initModule() -> UIViewController {
          let interactor = RocketLaunchInteractor()
          let viewModel = RocketLaunchViewModel(coordinator: self, interactor: interactor)
          let viewController = UIStoryboard.loadViewController() as RocketLaunchViewController
          viewController.viewModel = viewModel
          return viewController
     }
      
     func showLaunchProgressModule() {
          let viewController = LaunchProgressCoordinator().initModule()
          viewController?.presentViewController(viewController, animated: true)
     }
}

That’s it!

 

Megazord transforming with lightnings around it.

MVVM is simple and we can maintain a design pattern that is similar in both mobile platform. It defines how things connect and keeps all the code in its place according to what they should be doing.

Is it better than VIPER?

Yes – this is my short answer!

The long explanation is: for some time VIPER has been our design pattern to be used as the default structure of the modules of mobile apps at CKL. VIPER has brought established solutions to some common issues we have being through. It decouples things, so they can be tested. And defines responsibilities for every layer, so we know where to put code, avoiding the famous Massive View Controllers.

So, what’s wrong with it?

VIPER has too many layers. This started to become a problem. Every new module needs a big set of classes, even when the module is a simple one, like a login page. There are too many things we need to plug and a lot of abstraction layers to understand.

MVVM brings all the testability and decoupling we need in a smaller and more simple to understand pattern. MVVM core has only 3 layers. And the name of these layers explain themselves. Of course we added Coordinator and Interactors to the set. But the core of the pattern is simple and the same for both platforms.

Also, I don’t like snakes. ????

Gif of a Megazord holding a sword.

References

About the author.

Ricardo Gehrke Filho
Ricardo Gehrke Filho

I’m a mobile developer who loves giving life to smart and beautiful apps. I also appreciate a good walk at the beach, cold beer, burger and fries, good music and talk about life, the universe and everything else.