Article in Android App Development category.

Why MVI? —The State Problem

To understand what the State problem is, let’s first have a look at what is State and how we can model it for a particular…

This is the second article in Why MVI series. You might be interested to read about the motivation, for that you can have a look at the introductory article here. To understand what the State problem is, let’s first have a look at what is State and how we can model it for a particular screen.

State

Any variable which is used to depict what a view component is currently doing is its state. View is currently refreshing. Login Button enabled. Current Page NumberList of Items. Error Message. Is User Logged In. etc. Altogether, these constitute the state of any screen.

In MVI, State is the single source of truth. Instead of View, Presenter and Data Layer having their own states we have just one State object which represents the state of the whole screen.

Let’s try to model a State object for a screen.

the state problem in mvi items fragment

ItemsFragment

We have a screen called ItemsFragment as shown in the diagram. We will try to model a State object for this screen. For simplicity we are not considering the TabLayout or BottomNavigationView and instead going to just focus on the highlighted part.

Loading

The progress indicator which indicates that the items are currently loading can be represented as a Booleanloading.Currentlythe List of items is empty.

EmptyStateVisible

After getting the results from our Repository, we could get an empty result which will show an empty state on the screen. It can be represented as a BooleanemptyStateVisible.

Refreshing

User can also pull to refresh the screen which can be represented as a Boolean : refreshing. Do note that loading and refreshing are two separate states as they represent state of different view elements.

No Internet

Next we have an error state of the view which can be represented as a String :errorMsg on the screen. It is currently set to “No Internet”.

Items List

In case our view shows some items, our list is going to be non-empty and state will contain List<Item>.

State Problem

Let’s now have a glimpse of a simple state problem in an app below.

We are loading some items and we thus see a loading indicator. Then, we get an empty result from the server and we show an empty state to the user which says “No Items”. The user then hits Pull to Refresh and even though we get an empty response from the server again. The pull to refresh indicator keeps showing forever.

This is a simple example of a state problem from one of my own apps. These kinds of issues can creep into apps from seemingly nowhere. I have seen many good apps with issues such as Error State and Loading indicators shown at the same time.

Misusing MVP

Let’s figure out how we caused the above issue by misusing our favorite pattern.

Representing MVP

This Diagram shows various elements in MVP + User. Time is represented on the y-axis and increases from top to bottom.

The user first opens the app (start()) and View starts loading data. View calls showLoading() on itself which shows a ProgressBar on the screen. View changes its own state without Presenter knowing anything about it. This is one of the ways in which we can misuse MVP.

View then calls loadItems() on the Presenter. Presenter gets data from the Repository and we get an empty result. Presenter calls showEmptyState()and hideLoading() on View.

Presenter tries to drive the state of the view and some of the state is kept in Presenter while some is kept in the View.

The user hits pull to refresh on the screen (refresh()). View calls setRefreshing() on itself. Again View changes it’s own state without Presenter knowing anything about it.

View then calls refresh() on the Presenter. Presenter gets data from the Repository and we get an empty result. Presenter calls showEmptyState()and hideLoading() on view.

Presenter knows nothing about the refreshing state of view.

Presenter only knows about the MVP contract which exposes a method called hideLoading() and that’s what Presenter calls.

There’s no loader to hide and thus the Pull to Refresh indicator keeps on showing forever and we run into a State Problem.

What went wrong?

  • Presenter, View and even Business Logic: everything has its own State.
  • Presenter coordinates View State.
  • Presenter has multiple outputs and as the complexity of screen increases, it becomes hard to manage these output sites from Presenter.

Solution

If you remember, we created a single state object depicting the above problem. If there’s a single source of truth between various layers, there is only a single place where it can be modified. If there is only a defined set of actions that can be performed and for each action, there’s a defined way to change the state of our system. We can then solve this problem. This is what MVI suggests.

In the next part of the series, we will look at how we can extend our existing patterns and apply the guidelines from the MVI world to solve the problem above without having to start from scratch. A sample repository can be found here.

Recent Articles

Previous post Fueled’s Guide To GDPR For Mobile Applications May 24, 2018
Next post Moved May 31, 2018