---
title: A View State Machine for Network Calls on Android
teaser: Using Kotlin's sealed classes we can make a state machine for all UI-related
  to network calls.
tags: android,kotlin,mobile
author: Amanda Hill
published_on: 2017-03-23
---

As Ben Franklin once said, "In this world nothing can be said to be certain,
except death and taxes [and that software design specs always change]".

The reality of ever-changing design specs can, at times, be frustrating and
miserable, but rather than focus on the negatives I like to think that it can
also be a fun challenge in how we approach development. So today I am
going to share one improvement I made to how I handle network requests in
Android development to protect against UI changes.

## MVP

MVP (Model View Presenter) is a popular architectural style for Android
development. And while there are many positives - easy to
test, good separation between views and model - there are also some
downsides. One particular frustration I have faced is having to update the
view interface every time you want to update another UI element. For example,
if you were building a profile page you might have the following view
interface:

```kotlin
interface ProfileView {
  fun showFirstName(name: String)
  fun showLastName(name: String)
}
```

But if you wanted to update the UI to also show a user's age and astrological
sign you would also have to update the view interface.

```kotlin
interface ProfileView {
  fun showFirstName(name: String)
  fun showLastName(name: String)
  fun showAge(age: Int)
  fun showAstrologicalSign(sign: String)
}
```

This update to the view interface is both frustrating and a violation of the
[`Open-Closed Principle`](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)).

**Note**: Before I continue talking about how annoying updating view
interfaces can be, I want to admit that, just like everything else in life,
there are pros and cons, and sometimes this sort of update is necessary and can
even be good.

But enough mature pragmatism, back to the bad... Another reason I find these
sorts of updates frustrating is that they can take away from the bigger picture
of what is happening in a given view and focus too much on the minute details
of visual implementation. Now I know what you're thinking, "but it's a VIEW
INTERFACE! It's supposed to focus on the visuals!". And you would be correct,
but before you get a bee 🐝 in your bonnet 👒 consider that we do not yet have the
full picture. So far in our example we have only been focusing on the methods
that handle displaying a `User`. Let's look at the presenter, to see how it
fetches the `User` to better understand the rest of what is happening
with this view.

```kotlin
class ProfilePresenter(val view: ProfileView, val api: ProfileDataStore) {

  fun fetchUser() {
    api.fetchUser()
        .subscribe({ user ->
          // network call successful
          view.showFirstName(user.firstName)
          view.showLastName(user.lastName)
          view.showAge(user.age)
          view.showAstrologicalSign(user.sign)
        }, { error ->
          // network call failed
        })
  }
}
```

Ah good ol' network calls. Because our `User` object is coming
from a network call, we need to update our view interface to include methods
for showing the various states surrounding making a request.

```diff
interface ProfileView {
  fun showFirstName(name: String)
  fun showLastName(name: String)
  fun showAge(age: Int)
  fun showAstrologicalSign(sign: String)
+ fun showError(errorMessage:String)
+ fun showLoading()
+ fun hideLoading()
}
```

 Our view interface is starting to look a bit different. It's not all about the
`User` details any more, its also got some networking view state stuff in
there. As you can imagine all view interfaces that work with a presenter that
makes networking calls would also have to add these networking related view
methods. That means a lot of duplication across all our view interfaces and no
one wants that 🙅.

## Networking View State

Say hello to `NetworkingViewState` 👋. A [sealed
class](http://kotlinlang.org/docs/reference/sealed-classes.html) that abstracts
all the various states a view can be in, as a result of a networking call,
into a single type.

```kotlin
sealed class NetworkingViewState {
  class Loading() : NetworkingViewState()
  class Success<T>(val item: T) : NetworkingViewState()
  class Error(val errorMessage: String?) : NetworkingViewState()
}
```

Now let's update our view interface with this new type.

```kotlin
interface ProfileView  {
  var networkingViewState: NetworkingViewState
}
```

And our presenter...

```kotlin
class ProfilePresenter(val view: ProfileView, val api: ProfileDataStore) {

  fun fetchUser() {
    view.networkingViewState = NetworkingViewState.Loading()
    api.fetchUser()
        .subscribe({ user ->
          // network call successful
          view.networkingViewState = NetworkingViewState.Success<User>(user)
        }, { error ->
          // network call failed
          view.networkingViewState = NetworkingViewState.Error(error.message)
        })
  }
}
```

Now if the UI changes, our view interface, our presenter and our tests do not
have to change, because the bigger picture behavior remains the same 💪

But were not done yet... While we have made our code more SOLID (get it!?) we
have also lost some test coverage. Before we introduced `NetworkingViewState`
our presenter was in charge of mapping the `User` object we got in the response
from the API into the appropriate types to be shown by the view interface. And
because those formatted values were passed through the view interface we could
assert they were the correct values in our tests.

Like this:

```kotlin
class ProfilePresenterTest() {

  val view = mock<ProfileView>()
  val dataStore = mock<ProfileDataStore>()
  val mockUser = User("Amanda", "Hill", 100, "Aquarius")

  @Test
  fun testFetchUser_success() {
    //stub network response
    whenever(dataStore.fetchUser()).thenReturn(Observable.just(mockUser))

    val presenter = ProfilePresenter(view, dataStore)
    presenter.fetchUser()

    verify(view).showLoading()
    verify(view).showFirstName(mockUser.firstName)
    verify(view).showLastName(mockUser.lastName)
    verify(view).showAge(mockUser.age)
    verify(view).showAstrologicalSign(mockUser.sign)
    verify(view).hideLoading()
  }
}
```

But with our updated presenter our tests now look like this:

```kotlin
class ProfilePresenterTest() {

  val view = mock<ProfileView>()
  val dataStore = mock<ProfileDataStore>()
  val mockUser = User("Amanda", "Hill", 100, "Aquarius")

  @Test
  fun testFetchUser_success() {
    //stub network response
    whenever(dataStore.fetchUser()).thenReturn(Observable.just(mockUser))

    val presenter = ProfilePresenter(view, dataStore)
    presenter.fetchUser()

    verify(view).networkingViewState = isA<NetworkingViewState.Loading>()
    verify(view).networkingViewState = isA<NetworkingViewState.Success<User>>()
    verifyNoMoreInteractions(view)
  }
}
```

There is no validation around the `User` object being passed on the success
case. So how can we gain those formatting tests back?

![view model meme](https://images.thoughtbot.com/blog-vellum-image-uploads/3mdee2n8Sn6iXrVCtLzD_viewmodel-meme.jpg)

That's right, view models! When we set the `NetworkingViewState` to `Success`
in our presenter, rather than pass the `User` object we can pass a
`UserViewModel` instead. And we can add tests for our `UserViewModel` in a
separate class to ensure all values are formatted properly.

Something like this:

```kotlin
class UserViewModelTest {

  val mockUser = User("Amanda", "Hill", 100, "Aquarius")

  @Test
  fun testName() {
    val viewModel = UserViewModel(mockUser)
    val expected = "Amanda Hill"
    val actual = viewModel.fullName()

    assertEquals(expected, actual)
  }
}
```

Huzzah 💪 ! Test coverage regained 💃 ! And blog post complete 🤓 !
