The ultimate goal of a developer is to code simple, understandable and optimized. So to make a code with all these qualities there comes an idea of modular programming. The goal is to separate functionality into independent, interchangeable modules.
That's cool, so how to achieve modular programming? This paved way for several architecture patterns. Architecture patterns are blueprint with a set of rules to follow. These patterns evolved through the mistakes done while coding over years. Below are the few architecture patterns widely used in Android.
- Model View Controller(MVC)
- Model View Presenter(MVP)
- Model View View Model(MVVM)
The latest addition to these patterns is Model View Intent(MVI). To understand MVI, you should know how other patterns work.
Brief workflow of MVI:
User interaction with the UI is processed by business logic which brings change in the state. This new state is rendered on view and this newly updated view is shown to the user. This leads to a Unidirectional and Circular Data Flow.
As I mentioned above, patterns evolved through the mistakes done while coding over years. Let us have a look at what are all the absolute advantage of MVI over other patterns, which gives us the answer to the question Why MVI?
M odel as State: UI might have different states - Loading State, Data State, Error State, User Scroll Position etc. In MVI, models are formalized as the container of states which is not the case with other patterns. Every time a new immutable model is created which is then observed by the view. This way of creating an immutable model will ensure thread safety.
Single Source Of Truth: Each component might have its own state. View and Presenter/ViewModel are meant as the component here. Maintaining states separately at different levels might cause state conflicts. So to avoid these kinds of issues, the state is created at one level (Presenter/ViewModel) and passed to another level (View) in MVI. The only way to change the state is by firing an Interaction by the user. This kind of approach restricts any change to the State of the system only via a defined set of actions. An undefined action by a user cannot cause any undesired change to our System.
No Callbacks: Usually views are updated via callbacks after the asynchronous call. The view might be detached when the asynchronous call brings the result. So to avoid the crash or improper behavior of the application, we would check whether the instance of the view is available and it is attached. This will pave way for some boilerplate code like below.
These checks and callbacks are handled automatically in MVI using reactive programming that supports observer paradigm.
Hunt the crash: You might be faced situations when it is very difficult to trace and reproduce the crash even though if you have the crash report. The crash report might have the trace of the code flow but does not contain the trace of the view’s state flow before crashing. In MVI tracing the crash becomes easy with the State(Model). In the below code snippet if you see inside the doOnNext closure, logs are registered with the respective action and with the State. Any developer can reproduce the crash with state trace and fix it easily.
Brings back the state: A good app should work without any glitch even in unpredictable circumstances. Suppose orientation change or the activity process gets killed during a phone call, the android activity gets recreated. Restoring the state back was a big task at such situations. MVI library from Mosby maintains the current state and restore the state when the view gets recreated.
Independent UI Components: Every architectural pattern preach to us how the components should be built with no dependencies. The View/Presenter should not be coupled with one another. The responsibility of the View and Presenter is just to render the content and map the data to view respectively.
“From my point of view, Parent-Child relations are a code smell, because they introduce a direct coupling between both Parent and Child, which leads to code that is hard to read, hard to maintain, where changing requirement affects a lot of components” - HANNES DORFMANN
There is a situation when data must flow from one component to another. Is this possible without direct coupling of the components? Yes, it is possible using MVI - a Reactive Architecture Pattern. Using observable, the components observe the same model. Components get the notification if the model gets updated.
Multi-platform standard: Reactive programming is a multi-platform standard, so whether it’s a Web or Android or IOS, you might end up joining a multi-platform discussion on Rx. Reactive Codes written in Android(Kotlin) can be shared with IOS(Swift) and vice-versa which makes the code review task easy.
Hands-on Learning is the philosophical term behind the idea of immersing oneself in a subject in order to learn. Hands-on Learning has been around since 350 BCE, when Aristotle wrote, “for the things we have to learn before we can do them, we learn by doing them”. Created a project to understand and learn the concept of MVI. You can check this sample project.
About the Project:
Displays the restaurants available in your location. Used Zomato api to retrieve the restaurants details.
Project Flow :
- Emits user action.
- Observes the state change.
- Render the UI with updated changes.
- Catch user action emitted by the view.
- Make a call to the respective Business Logic Layer.
Business Logic Layer:
- Process the request from the presenter.
- Do asynchronous call.
- Gives the result to Reducer.
Reducer: Takes both the previous and the current state as parameters and returns a new state. For each action, the reducer returns a new copy of the immutable state.
If you found this post useful, it would help others to find it if you could click on the 👏 icon below and also do follow for more articles like this — thanks!