• slidebg1

MOVVER iOS Architecture: Beyond MVVM (but not so VIPER far)


Selecting an iOS architecture that fits your needs is not a simple issue. In last years, MVCMVVM and lastly, VIPER architectures have been proposed as a solution to clean architectures. At Develapps, we have been playing around with all of them, and we are currently using MOVVER: MOdel-View-Viewmodel-Router. Want to now why?

Come here, and I will show you

Come here, and I will show you

Classic iOS Clean Architectures

MVC

Ok, let's be honest: MVC is far from being clean, but it is the Apple suggested architecture. Hence, lots of people start working this way, but then, they have to face the tough time when they realize they cannot test their UI behavior in an easy way. Ok, there are UI Tests, but if you have used them, you will agree with me that you cannot be confident with them. They may surprisingly fail some times and pass others.

So, why is the reason you cannot Unit test iOS MVC? Because most of your business logic is inside your controller, also know as Massive View Controllers. Everything is inside a ViewController! You can get single ViewController files bigger than a full Framework, and moreover, you cannot test your UI in a Unit Test. What can you do then? You have to break your Massive View Controller into pieces, following the single responsibility principle of the well-known SOLID principles.

 

MVC - Apple MVC - Apple

MVVC

MVVC is the rational and easy way of breaking down this MVC architecture. You create a new player called ViewModel (A great introduction to MVVM by Ash Furrow can be found here).

MVVC Architecture - Objc.io

MVVC Architecture - Objc.io[/caption]   The ViewModel has two main responsibilities: On one hand, receiving events from the controller -and act accordingly with your business logic- and on the other hand, update your ViewController with a valid transformation of your model.

Show me the code!

All right, let's say you have a UIButton and a UILabel. The classic one. You push the button, receive some info from a network call and show it at your label. So lets have a look to some code.

First, we declare protocols to glue your view and viewModel, first the viewModel protocol. There are other options to this bindings, like ReactiveCocoa or KVO:

// Your viewModel will adopt this protocol 
@protocol ButtonViewModelProtocol 
 -(void)buttonPressed;
 @end

Then the protocol for you view:

// Your view will adopt this one
 @protocol ButtonViewProtocol 
 -(void)showTheInfo:(NSString*)label;
 @end

Okay! We have our protocols ready. Now we have to setup our viewController, outlets -if using Interface Builder-, etc. Then we create the ViewModel. The viewModel is a PONSO (Plain NSObject) which will allow us to test it:

// A viewModel is a plain PONSO, and it receives the view so it can tell the view to show the text at the label 
// Keep in mind you will never use UIKit objects in a ViewModel, that is the reason we define protocols and we do not 
// pass a plain UIViewController here.
 @interface ButtonViewModel : NSObject 
 @property (nonatomic,weak) id buttonView;
 @end

The implementation receives as first responsibility to react to user input (button press) and to update the view with a user-formatted value of your model.

 @implementation ButtonViewModel 
// First responsibility: Honoring the protocol, we receive the button action implementing buttonPressed
 -(void)buttonPressed{
 NSObject *model;
 // Do some stuff here to get your logic
 // Second responsibility: transform your model to a suitable format to be shown to the user.
 [self.buttonView showTheInfo:[NSString stringWithFormat:@"This is the model: %@",model]];
 }
 @end

Viper

VIPER (View-Interactor-Presenter-Entity-Router) is another step towards a decoupled and clean architecture. Following the single responsibility principle, basically what it states is breaking down the ViewModel into the Presenter-Interactor couple, introduces the Wireframe (AKA Router). If you want a more in-depth explanation of VIPER, a good one can be found here. A basic summary of VIPER could be:

  1. View: Views, ViewControllers, etc. All UIKit related stuff goes here.
  2. Wireframe (Router): Transitions, segues, etc. All "move to this screen" or "show this alert" stuff goes here.
  3. Presenter: It formats all the data to be presented by the View which is coming from Interactor and passes the events from the View to the Interactor.
  4. Interactor: Gets/sends data to the data stores (Coredata, Network, Caching, etc.), receives events from the presenter and executes business logic of the use case. Notifies the presenter of the events coming from data stores.
  5. Entities: They are your Model objects (Model from the MVC/MVVM architecture).

VIPER Schema - MutualMobile VIPER Schema - MutualMobile

Aiming a Testable and Clean Architecture

Moving to MVVM

When at Develapps we tried to move from a non-testable and hard-to-maintain architecture like MVC we followed the easy path: Starting with MVVM. That was an easy step. You just have to keep in mind that:

  • Your ViewModels cannot publicly expose model objects. Plain and simple. Any interaction with your Views should be using protocols.
  • Your ViewModels cannot include UIKit objects. Moreover, do not include UIKit at all.

Following this two principles, ViewModels are really good places for Business Logic testing. If you want to try a MVVM architecture, I suggest you start developing in a TDD way:

  1. Write the test of the ViewModel.
  2. Add the public interface of your ViewModel.
  3. Let it fail. You have not written a single line of code, so the tests should fail.
  4. Write your ViewModel code.
  5. Run the test.
  6. Repeat the full process for the next Use Case.

If you follow this approach, it will be really easy to develop your viewModels. I suggest you to stub your network code -using OHHTTPStubs it is really easy- to avoid network dependencies at the beginning of your project. There is no point testing your code against your backend if it is in an early stage. Later on you can test your code against the real backend.

Testable Code

The next step, VIPER?

When we were working some time with MVVM, we realized that some times, not having network code close to you forces you to create a lot of boilerplate code. Say, for example, you want to push a UIViewController when you press a button inside a UITableViewCell -I call this Cell View Model Hell-. The process will be this one:

cellVM Hell cellVM Hell

This is, you have to create 3 protocol's method for a button push ?. So, if we move to VIPER, this case is easier, as you can pass the router to your cell's presenter:cell's case in VIPER 

There is other drawback for MVVM: When you have to pass the model between UseCases, you have to pass it to the next ViewController, which will do nothing with it, but send it to the viewModel. In VIPER, you have to pass it through the router, which is even worse.

Both architectures are much better than MVC. Nevertheless, VIPER was like a bit too much for some of out projects, and many times it feels pointless to break ViewModels into Presenter-Interactor couples. Therefore we refactored the best from VIPER and MVVC architectures.

MOVVER

Let me introduce you to MOVVER (Model-View-ViewModel-Router). A picture is worth a thousand words:

MOVVER Architecture - Develapps MOVVER Architecture - Develapps

So, the components are:

  1. View: Your viewControllers, Views, etc. It retains the ViewModel.
  2. ViewModel: Exactly the same as in MVVM, but it receives a Router on initialization, and retains it.
  3. Router: The router provides UI Transitions and also is responsible of building the use case. It is instantiated by a previous router, which passes any Model needed to the new router, instantiates the ViewController, instantiates the ViewModel object with the received model, passes the ViewModel to the Controller, and passes itself to the viewModel. Sounds like a lot, but they are few lines of code, and get's the best from VIPER and avoids the worse from MVVM.
  4. Model: The models you use in your App. We usually create a provider object which is used throughout all the app. It provides Models for the ViewModel by using one or some datasources which could be Coredata, Network, etc.

Final Thoughts

Moving from MVVM to MOVVER was like a natural step, but we felt that we didn't need to move a step further than that. To be honest, MOVVER does not respect the single responsibility principle, but there are no drawbacks in this case as far as we know. There is little more code than MVVM and you feel like you have all the power of VIPER.

I know Kung Fu

Keeping in mind some interesting strategies, you can destroy your Massive View Controllers, keep -the majority of- your code testable, and not putting too much boilerplate code.

Públicado el 23/05/2016

Comparte este post:

CATEGORÍAS: Desarrollo