Fighting state redundancy in Model-View-View-Model

Alexey Naumov
Oct 21 · 4 min read

One of the most common practical problems in mobile apps is loading displayable data from the server, where the data can be anything from user’s feed or a list of podcasts to a profile picture or a streaming video.

Apps show various spinners and bars to indicate the loading process, all for inducing user’s patience and improving their experience.

However, this seemingly trivial problem has hidden pitfalls for a programmer, where a naive implementation can lead to writing excessive code and fixing sudden bugs.

Let’s take a simple example where we need to load and display a list of podcasts using Model-View-ViewModel pattern for the screen structure with RxSwift for UI binding.

Model

An immutable struct for holding basic info about a podcast record:

The networking layer is represented with this tiny protocol; I’ll omit the factual networking code for simplicity of the example:

ViewModel

For the list of podcasts, we’ll need to have an observable array of Podcaststructures. For that, we wrap [Podcast] in the BehaviorRelay class from RxSwift, which is basically an observable property that always holds a value:

As you can see, we provided the ViewModel with access to the networking layer through a reference to PodcastsService.

The array of Podcast records is initially empty, but loadPodcasts() function allows the user of the ViewModel to query the podcasts at the right time, and as the request completes it updates the list of podcasts.

View

A simple TableViewCell for displaying the Podcast info:

And finally, the ViewController that shows the list of podcasts:

Considering that the viewModel is instantiated and injected to the ViewController from the outside, now we have a fully functioning MVVMmodule that automatically loads and displays the list of podcasts.

The only problem is that the app is currently not user-friendly. It doesn’t have an indicator for the loading process; neither does it show an error that can possibly occur in the networking layer.

Let’s update the ViewModel to address the challenge:

We’ve added isLoading boolean property for tracking the loading status, and a onError PublishRelay for reporting an error.

Now we need to update the UI and bind it with the status updates:

OK, now we’re showing UIActivityIndicatorView when viewModel.isLoading reports that the podcasts are loading, and there is a UILabel for showing errors emitted by viewModel.onError

Note, that in the lines 16–17 we also had to explicitly hide the errorMessageLabel when podcasts has any records, because otherwise, the error message would stay on the screen even if the podcasts request succeeds after it initially errored out.

You can already sense a code smell. For such a simple use case we’ve already started fighting the state inconsistency.

Let’s take a deep breath in and out, and think for a minute. What are the possible states that our screen actually can be?

  1. Podcasts have not yet been queried
  2. Podcasts are being loaded
  3. Podcasts have failed to load
  4. Podcasts were loaded successfully

That’s right, just four cases! The superposition of the state values we have right now (podcasts being empty or not, isLoading and onError) gives us 2*2*2 = 8 possible cases. This state redundancy is the reason why we had to implement a fix for conflicting values of podcasts and onError. In fact, this means there are other combinations we didn't consider yet, which can lead to unwanted effects, such as a simultaneous display of the error message and the loading indicator.

As we’ve identified the problem, we can refactor the current implementation to purge the state redundancy by introducing enum with those four cases:

This neat enum allows us to rework the ViewModel in the following way:

With just one variable we were able to represent all the states we had, but now — without the state redundancy.

Refactoring the ViewController:

For convenient mapping the Loadable<[Podcast]> in the code above we can use an extension for the Loadable:

Great! Now our state — UI binding is much more clear and doesn’t require an excessive code for fixing visual defects.

If we want to display the previously loaded list while performing the list refresh, we can extend the isLoading case with a parameter for holding the value.

The final implementation of the Loadable, as well as a few more perks for it, can be found in the gist on Github; however, this was just an example of how to improve the clarity and stability of the reactive code by eliminating the state redundancy in the ViewModel.

Originally published at nalexn.github.io

Let’s connect

You can find me on LinkedIn or Twitter

Flawless iOS

🍏 Community around iOS development, mobile design, and marketing

Alexey Naumov

Written by

iOS developer since 2011

Flawless iOS

🍏 Community around iOS development, mobile design, and marketing

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade