A production-level architecture for Android apps (part 2)
Tags: android, mvvm, kotlin, architecture
Published: 2020-02-15 22:07:36

# Previous

{% link 4gus71n/a-production-level-architecture-for-android-apps-part-1-271m %}

# Index
- [Context](#context "Context")
- [Layers](#layers "Layers")
- [DataSources](#datasources "DataSources")
- [Repositories](#repositories "Repositories")
- [Interactors](#interactors "Interactors")
- [ViewModel](#viewmodel "ViewModel")
- [View](#view "View")
- [Notes](#notes "Notes")
- [Next](#next "Next")

# Context

We are going to go through all the different **layers** of the architecture, explaining what’s the **protocol** and the **logic** of each layer. The protocol is what the layer exposes to the above layer. The logic of the layer is what the layer is supposed to do internally.

# Layers

>“This architecture looks like an overkill, you can achieve the same thing using less layers, and it will scale properly.”

It may, it may not. I’m used to working on large-scale projects, where the requirements may change from one day to the next, where you may have to tweak features all the time. That’s why I try to go with the most flexible architecture possible. This is what I’ve been using and it has worked fine for me. I prefer to waste 5 minutes creating some extra classes today, that spend 1 day trying to wire something up, so it just works.

## Datasources

### Protocol

The protocol of the data sources is pretty straightforward, **fetch data** from somewhere.

### Logic

There are only two places from where we can fetch information, from the **network** (through API endpoints or some other service) or from the **cache** (either through something like the `SharedPreferences`, a database, or some other local source)

#### Network Logic

Fetching information from a **backend** API through RESTful endpoints. For this example, I’m using Retrofit. There are some other tools out there like Volley, but discussing the pros and cons of each tool goes beyond the scope of this article.

#### Code example

With Retrofit, you usually just declare an **API Interface** that represents the available endpoints.

```kotlin
/**
 * Retrofit's interface that we use to communicate with a mock API hosted on apiary.io
 */
interface ExampleApi {
    /**
     * Fetches a paginated response containing the news.
     */
    @GET("news")
    fun fetchNews(
        @Query("page") page: Int
    ): Single<Response<ApiNewFeedListResponse>>

    /**
     * Fetches the most recently viewed news.
     */
    @GET("news/recent")
    fun fetchRecentlyViewedNews(): Single<Response<ApiNewFeedListResponse>>

    /**
     * Fetches the user profile information
     */
    @GET("profile")
    fun fetchProfileInfo(   ): Single<Response<ApiProfileResponse>>

    /**
     * Fetches the user business skills
     */
    @GET("business")
    fun fetchBusinessSkills(): Single<Response<ApiBusinessSkilssResponse>>

    /**
     * Fetches a list of cooking recipes
     */
    @GET("recipes")
    fun fetchAllRecipes(): Single<Response<List<ApiRecipeResponse>>>
}
```
`ExampleAPI` interface to fetch information from a mock API hosted on [ApiAry](https://apiary.io "ApiAry").

```kotlin
/**
 * Retrofit's interface API that we use to communicate with the Imgur API.
 */
interface ImgurApi {

    /**
     * Fetches the comments associated with one particular post.
     * @param id The id of the post.
     * @param sort "best" if we want to fetch the comments sorted by the most up voted. "new" if
     * we want to fetch the comments sorted by date.
     */
    @GET("gallery/{id}/comments/{sort}")
    fun fetchComments(
        @Path("id") id: String,
        @Path("sort") sort: String // best, new
    ) : Single<Response<ApiCommentsResponse>>

    /**
     * Fetches the posts using the specified search criteria.
     * @param sort "top" If we want to fetch the latest posts. "viral" If we want to fetch the
     * most trendy posts.
     * @param window If sort equals "viral" this parameter gets ignored. If sort equals "top" we
     * can specify if we want to fetch the most important posts from this "month", "day", or "week"
     * @param query The search query.
     * @param page The page number, starts from 1.
     */
    @GET("gallery/search/{sort}/{window}/{page}")
    fun searchGallery(
        @Path("sort") sort: String = "top", // viral, top
        @Path("window") window: String = "all", // if sort == top { day, week, month }
        @Path("page") page: Int = 1,
        @Query("q") query: String = ""
    ) : Single<Response<ApiImgurGalleryResponse>>

    /**
     * Fetches the posts using a tag.
     * @param sort "top" If we want to fetch the latest posts. "viral" If we want to fetch the
     * most trendy posts.
     * @param perPage Max amount of results per page.
     * @param tag The tag query.
     * @param page The page number, starts from 1.
     */
    @GET("gallery/t/{tag}/{sort}/{page}")
    fun searchTags(
        @Path("sort") sort: String = "top", // viral, top
        @Path("page") page: Int = 0,
        @Path("tag") tag: String = "",
        @Query("perPage") perPage: Int = 20
    ) : Single<Response<ApiImgurTagResponse>>
}
```
`ImgurAPI` interface that we use to fetch image posts from through the [Imgur API](https://apidocs.imgur.com/?version=latest "Imgur API").

Notice that on each API call we return a `rx.Single`, that’s because when we hit an API endpoint we send a request and get a *single* response.

The second step is to expose the Retrofit dependency to all our other dependencies, we do this through the network modules ([ExampleNetworkModule](https://github.com/4gus71n/Examples/blob/master/core/src/main/java/com/kimboo/core/di/modules/ExampleNetworkModule.kt "ExampleNetworkModule") and [ImgurNetworkModule](https://github.com/4gus71n/Examples/blob/master/core/src/main/java/com/kimboo/core/di/modules/ImgurNetworkModule.kt "ImgurNetworkModule")) and the [RetrofitModule](https://github.com/4gus71n/Examples/blob/master/core/src/main/java/com/kimboo/core/di/modules/RetrofitModule.kt "RetrofitModule").

On the network modules we build the REST client and we configure all the things that we need.

```kotlin
@Module
class ExampleNetworkModule {
    @Provides
    @Singleton
    fun provideGson(): Gson {
        return GsonBuilder()
            .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
            // Gson deserializer to deserialize dates from the Imgur API
            .registerTypeAdapter(Date::class.java, DateDeserializer())
            .create()
    }

    @Provides
    @Singleton
    fun provideOkHttpClient(): OkHttpClient {
        val builder = OkHttpClient.Builder()
        return builder.build()
    }

    @Provides
    @Singleton
    fun provideRetrofit(okHttpClient: OkHttpClient, gson: Gson): Retrofit {
        return Retrofit.Builder()
            .baseUrl("http://private-cc89a2-examples21.apiary-mock.com/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(okHttpClient)
            .build()
    }
}
```
`ExampleNetworkModule` where we provide all the dependencies that we need to fetch data from the ApiAry backend.

```kotlin
@Module
class ImgurNetworkModule {
    @Provides
    @Named("authInterceptor")
    fun providesAuthInterceptor(): Interceptor {
        return Interceptor { chain ->
            var request = chain.request()
            request = request.newBuilder()
                .header("Authorization", "Client-ID 6ea78556ea84b48")
                .build()
            chain.proceed(request)
        }
    }

    @Provides
    @Singleton
    @Named("imgurOkHttp")
    fun provideOkHttpClient(
        @Named("authInterceptor") authInterceptor: Interceptor
    ): OkHttpClient {
        val builder = OkHttpClient.Builder()
        builder.addInterceptor(authInterceptor)
        return builder.build()
    }

    @Provides
    @Singleton
    @Named("imgurRetrofit")
    fun provideRetrofit(
        @Named("imgurOkHttp") imgurOkHttp: OkHttpClient,
        gson: Gson
    ): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.imgur.com/3/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .client(imgurOkHttp)
            .build()
    }
}
```

We use **Gson** to deserialize the endpoint’s responses and turn them into **backend model classes** (naming convention  *ApiSomethingResponse*). We provide the **OkHttp** client that the Retrofit instance is going to use as their HTTP client. And finally, we provide the actual **Retrofit** instance that we are going to use to instantiate the API interface.

Notice that in the [ImgurNetworkModule](https://github.com/4gus71n/Examples/blob/master/core/src/main/java/com/kimboo/core/di/modules/ImgurNetworkModule.kt "ImgurNetworkModule") we need to provide an OkHttp instance that authenticates against the Imgur API. We need to provide different Retrofit instances on both modules, since one points to the Imgur base host and the other to the ApiAry host. Also, notice that we actually reuse the same Gson instance for both modules.

Then we have the **RetrofitModule** where we provide the API Interface instance for both the Imgur API and the ApiAry API.

```kotlin
@Module
class RetrofitModule {
    @Provides
    @Singleton
    fun provideExampleApi(
        retrofit: Retrofit
    ): ExampleApi {
        return retrofit.create(ExampleApi::class.java)
    }

    @Provides
    @Singleton
    fun provideImgurApi(
        @Named("imgurRetrofit") imgurRetrofit: Retrofit
    ): ImgurApi {
        return imgurRetrofit.create(ImgurApi::class.java)
    }
}
```

#### Cache Logic

Fetching information **locally**, either through the `SharedPreferences`, a DB or some other source. For this example, I’m using **Room**. There are some other tools that you can use like Realm or SQLDelight.

#### Code example

There are a few classes that we need to implement when setting up Room. The first is the **RoomDatabase** class, that basically handles the DB creation:

```kotlin
@Database(
    entities = [DbRecipeDto::class],
    version = 1,
    exportSchema = false
)
@TypeConverters(Converters::class)
abstract class ExampleRoomDatabase : RoomDatabase() {
    companion object {
        fun create(context: Context, useInMemory: Boolean): ExampleRoomDatabase {
            val databaseBuilder = if (useInMemory) {
                Room.inMemoryDatabaseBuilder(context, ExampleRoomDatabase::class.java)
            } else {
                Room.databaseBuilder(context, ExampleRoomDatabase::class.java, "example.db")
            }
            return databaseBuilder
                .allowMainThreadQueries()
                .fallbackToDestructiveMigration()
                .build()
        }
    }

    abstract fun recipesDao(): RecipesDao
}
```

Then we need to create the classes to map the **database responses** (naming convention *DbSomethingDto*) into objects.

```kotlin
@Entity(
    tableName = "db_recipe_dto",
    indices = [Index(
        value = ["id"],
        unique = true
    )]
)
data class DbRecipeDto(
    @PrimaryKey
    val id: Int,
    val name: String,
    val ingredients: List<String>,
    val isBookmarked: Boolean = false
)
```
In this example, we are mapping a table that stores information about cooking recipes.

Next, we have to create the DAO to query the information from the database (naming convention *SomethingDao*).

```kotlin
@Dao
abstract class RecipesDao {
    @Query("SELECT * FROM db_recipe_dto")
    abstract fun getAllRecipes(): Flowable<List<DbRecipeDto>>

    @Query("SELECT * FROM db_recipe_dto where id = :id")
    abstract fun getRecipeById(id: Int): Flowable<DbRecipeDto>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract fun storeRecipes(
        dbRecipeDtos: List<DbRecipeDto>
    ): Single<LongArray>

    @Update
    abstract fun updateRecipes(dbRecipeDto: DbRecipeDto): Single<Int>
}
```

Notice that if we want to keep track of the database changes we use `Flowables` to stream the query’s results. If we don’t care or if we want to listen for the results just once, we can use `rx.Single`.

## Repositories

### Protocol

The repository protocol is to **expose data** from the **data sources** through a **unified reactive interface** (`rx.Obsevable`). This data may come from the **network layer**, because we hit some endpoint, or may come from the **cache layer** because we ran some query on a database or fetched something from the database.

### Logic

It fetches the data from the data source and wraps the response with a `DataResponse`¹ class. This DataResponse class is used to carry around any information that we may need about the origin of this data (if it’s a cached response, if it came from the backend through a 304, or if it came from the backend through a 200, etc.)

### Code example

Let’s say that in our example app we need a repository to access all the information related to the **news**.

Keeping in mind that we have these two endpoints:

```kotlin
    /**
     * Fetches a paginated response containing the news.
     */
    @GET("news")
    fun fetchNews(
        @Query("page") page: Int
    ): Single<Response<ApiNewFeedListResponse>>

    /**
     * Fetches the most recently viewed news.
     */
    @GET("news/recent")
    fun fetchRecentlyViewedNews(): Single<Response<ApiNewFeedListResponse>>
```

Then the interface (*protocol*) of our repository would look like:

```kotlin
interface NewsRepository {
    fun fetchRecentlyViewedNews() : Observable<DataResponse<ApiNewFeedListResponse>>
    fun fetchNews(page: Int) : Observable<DataResponse<ApiNewFeedListResponse>>
}
```
Notice that the only thing that these repositories are supposed to expose are `rx.Observables`, doing that we are able to plug different repositories that fetch information from different sources through RxJava.

The actual implementation (*logic*) of our repository would look like:

```kotlin
class NewsNetworkRepository (
    private val retrofitApi: ExampleApi,
    private val uiScheduler: Scheduler,
    private val backgroundScheduler: Scheduler
) : NewsRepository {
    override fun fetchRecentlyViewedNews(): Observable<DataResponse<ApiNewFeedListResponse>> {
        return retrofitApi.fetchRecentlyViewedNews()
            .toDataResponse()
            .observeOn(uiScheduler)
            .subscribeOn(backgroundScheduler)
    }

    override fun fetchNews(page: Int): Observable<DataResponse<ApiNewFeedListResponse>> {
        return retrofitApi.fetchNews(page)
            .toDataResponse()
            .observeOn(uiScheduler)
            .subscribeOn(backgroundScheduler)
    }
}
```

The `toDataResponse()` function, is just an extended function from `rx.Observable` that wraps the stream on a `DataResponse`.

>Why making the Repositories return rx.Observables?

You need your repositories to have a sort of *plug-and-play* behavior. That means, using a reactive API (`zip`, `flatMap`, `merge`, `concat`, etc.) so you are able to **combine** different **repository calls** within your Interactors.

For example, if you need to create an Interactor that’s going to return both, the recently viewed news and all the news, you can *zip* the `fetchRecentNews()` and the `fetchNews(page)` calls together.

Another example, if you need to hit one endpoint and based on the response from that endpoint, hit another endpoint, you can just *flat map* the calls from both repositories and that’s it.

>That’s wrong, if you have to zip two API calls together, you should get the backend to return both responses together through one single endpoint.

Again, these are **real-world** scenarios, and unless you’re working for a company that solely relies on mobile as its source of income (Uber, Instagram, WhatsApp, etc.) mobile is probably going to be a *second-class* citizen. Meaning that you may not get all the endpoints that you need. That’s neither good nor bad, it is just how it works. But if you have to juggle API calls together, better to keep it tidy.

>Why using a response wrapper?

I’ve found that using a wrapper class to **keep track** of the **origin** of the data is very useful. That `DataResponse` wrapper that we have there is just a simple class that keeps track of where the data is coming from.

For example, let’s say that you want to do one thing if a specific endpoint returns a 401 (Not Authorized) and another if the endpoint returns a 500 (Internal Server Error). You can do that just by looking at the `DataResponse` wrapper.

Another example, let’s say that you’re storing the information on a cache layer if the endpoint replies with a 304 (Not Modified) you want to skip that from being stored on cache (because it’s the exact same information that you already have, so it’s going to be a waste of time storing that information gain). So using a wrapper class to keep track of the source of the information may come handy when trying to handle some specific scenarios.

> Why do you separate your repositories into interface and implementation?

That's something that I got used to from working with **clean architectures**. I can't say that it's super useful all the time, but I do like the idea of having a contract between a component's protocol and its implementation. Even if requires some extra work.

## Interactors

### Protocol

The interactor protocol is to **expose data** to the **ViewModels** through a **callback interface**. The data that we expose from the interactor to the ViewModel goes as **model classes**. While on the repository layer we may return backend model classes (ApiSomethingResponse) from the network API or database responses (DbSomethingDTO) from the database, the interactor returns plain model classes. By doing this we completely **decouple** the backend responses, the database responses and the actual model classes that we use across the app.

>That looks like an insane amount of work, keeping three different model classes for the backend responses, database responses and for using on the app. Why not just use the same class to map the backend responses (through Gson) and keeping the database tables?

Like many other topics that we talked about in this article, most of the layers are totally customizable. If you think that it is better to use one single model class for mapping the backend responses, the database tables, and for use across the app, you can do it. **My personal experience tells me otherwise**. Things may change on the backend, new fields may get exposed, some fields may get removed, you may need to track specific fields within the database, etc. I prefer to code a bit more of boilerplate code and be ready for changes that stick with a short solution and risk reliability.

#### Logic

An interactor uses one or more **repositories** to perform an action, this may be something like calling two different endpoints, wait for them to reply, and then return that information through the callback interface.

>Why using a callback interface here? Shouldn’t we expose a rx.Observables?

My personal experience is that it is better to hide everything related to RxJava beneath the interactor layer. If you are working on a large-scale project you may have some junior developers on the team, and working with technologies such as RxJava it’s a bit tricky for inexperienced developers. I find it more comfortable to keep all this hidden beneath the interactor layer, that way the most senior developers can word developing the interactors, and then the developers with less experience can just use them on the ViewModels.

Also, it saves you a bit of boilerplate code. Let’s say that you need to zip two repositories together to fetch data and show it on an Activity. If have to do the same thing again on some other Activity you would have to duplicate the code for zipping those repositories.

And finally, I think it looks tidier to have some sort of contract (callback interface) between the use cases and the ViewModels.

#### Code example

This is how the interface (*protocol*) of the interactor looks like:

```kotlin
/**
 * Fetches the recently viewed news and the paginated news altogether.
 */
interface GetNewsInteractor {
    interface Callback {
        /**
         * Triggered when we successfully fetch the paginated news
         * @param news The list of news for this page.
         * @param currentPage The current page.
         * @param totalPages The total amount of pages available.
         */
        fun onNewsSuccessfullyFetched(
            news: List<NewFeed>,
            currentPage: Int,
            totalPages: Int
        )

        /**
         * Triggered when we successfully fetch the recently viewed news.
         * @param news The list of recently viewed news.
         */
        fun onRecentlyViewedNewsFetched(
            news: List<NewFeed>
        )
        /**
         * Triggered when something goes wrong either fetching the recently viewed news or
         * the paginated news.
         */
        fun onErrorFetchingNews()

        /**
         * Triggered when we try to hit the endpoints without internet connection.
         */
        fun onNoInternetConnection()
    }

    /**
     * Execute function to trigger this Interactor.
     * @param callback A GetNewsInteractor.Callback usually implemented on the ViewModel.
     * @param page The current page that we are trying to fetch.
     */
    fun execute(callback: Callback, page: Int)
}
```
In this example, we see the `GetNewsInteractor`, an interactor that fetches a list of **recently viewed news** and **paginated news** through two different endpoints.

Here I'm just handling two error scenarios for when the API call fails and for when we try to hit the API without an internet connection. If you need to handle more in-detail errors, for example, a 401/403 error response from the API, you could just add a new `onNotAuthorizedError()` callback function here and implement the handling of that exception in the actual implementation.

The implementation (*logic*) looks like this:

```kotlin
class GetNewsInteractorImpl(
    private val newsRepository: NewsRepository
) : GetNewsInteractor {
    override fun execute(callback: GetNewsInteractor.Callback, page: Int) {
        // If we are fetching the first page either because we did a PTR or because this is the
        // first time opening this screen we need to fetch both
        if (page < 1) {
            fetchRecentlyViewedNewsAndPagedNews(page).subscribe(
                {
                    val pagedNews = it.first
                    val recentlyViewedNews = it.second

                    callback.onNewsSuccessfullyFetched(
                        news = pagedNews.list,
                        currentPage = pagedNews.current,
                        totalPages = pagedNews.total
                    )

                    callback.onRecentlyViewedNewsFetched(
                        news = recentlyViewedNews.list
                    )
                },
                {
                    if (it is ConnectException || it is UnknownHostException) {
                        callback.onNoInternetConnection()
                    } else {
                        callback.onErrorFetchingNews()
                    }
                }
            )
        } else {
            fetchPagedNews(page).subscribe(
                {
                    callback.onNewsSuccessfullyFetched(
                        news = it.list,
                        currentPage = it.current,
                        totalPages = it.total
                    )
                },
                {
                    if (it is ConnectException || it is UnknownHostException) {
                        callback.onNoInternetConnection()
                    } else {
                        callback.onErrorFetchingNews()
                    }
                }
            )
        }
    }

    /**
     * Fetches both, the recently viewed news from the /news/recent and the paginated news from the
     * /news?page={page}
     */
    private fun fetchRecentlyViewedNewsAndPagedNews(page: Int): Observable<Pair<NewFeedMetadata, NewFeedMetadata>> {
        return Observables.zip(fetchPagedNews(page), fetchRecentlyViewedNews()) {
                pagedNews: NewFeedMetadata, recentlyViewedNews: NewFeedMetadata ->
                Pair(pagedNews, recentlyViewedNews)
        }
    }

    /**
     * Fetches the recently viewed news from the /news/recent endpoint.
     */
    private fun fetchRecentlyViewedNews(): Observable<NewFeedMetadata> {
        return newsRepository.fetchRecentlyViewedNews()
            .map {
                it.response.toNewsFeedMetadata()
            }
    }

    /**
     * Fetches the paginated news through the /news?page={page}
     */
    private fun fetchPagedNews(page: Int): Observable<NewFeedMetadata> {
        return newsRepository.fetchNews(page)
            .map {
                it.response.toNewsFeedMetadata()
            }
    }
}
```

You can see that we call the `fetchRecentlyViewedNewsAndPagedNews(Int)` function which zips two calls from the `NewsRepostitory` and returns them wrapped into a `Pair` object.

Through RxJava you can manage to handle the responses however you want. For example, let’s say that, specifically, if the call to fetch the recently viewed orders fail you want to display a `Snackbar` on the UI, you can hook from the `doOnError()` and use the callback to communicate the ViewModel that something went wrong while trying to fetch the recently viewed news.

Also, you can pass any other dependencies that you might need. Let’s say that you want to track these errors through Crashlitycs, you can pass the Crashlitycs instance into the interactor and report the exceptions hooking from either the `doOnError()` or in the `onError()` from the `Subscriber`.

The same thing goes if the behavior of your interactor depends on some `SharedPreferences` configurations. You just pass the `SharedPreferences` instance through the interactor’s constructor and check whatever value you need and call one function from the repositories or the other.

>What about memory leaks? Don’t you dispose the Observable once you’re done with it.

Yes, disposing of the `Observables` as soon as you are done with them is a good practice. You can hook from the `onCleared()` function on the ViewModel and dispose of all the `Observables` through the interactor. For the sake of keeping things simple, I didn’t include an example of that on every interactor. But you can see an example of that in the `GetImgurPostsInteractor` and how we call the `dispose()` function from within the `ExampleImgurViewModel`.

## ViewModel

### Protocol

The ViewModel protocol is to **expose data** to the **view layer** through **LiveData** properties. These LiveData properties are usually MutableLiveData properties of **sealed classes** that represent the **state** of the ViewModel.

### Logic

There are a few little rules from the ADT about what we are supposed to do on a ViewModel and what not. One of them is:

- Do not use or handle any view-related classes on the ViewModel. The ViewModel is not supposed to know anything about the view layer.


Then there are a few rules that I came out with by myself:

- You can extend from the `AndroidViewModel` class and use the Application context, in case that for example, you need to fetch a String resource. I **highly discourage** using the `AndroidViewModel` class, because I think that anything related to fetching resources and things like this should be done on the view layer. Also, if you need to use an Android SDK class that isn't view-related, for example as the SharedPreferences, you should use them inside your interactors. There's no need to have this logic inside the ViewModel.

- Try to keep as few `LiveData` properties as possible. This makes the ViewModel easier to handle and easier to scale. Ideally, you should have a single `MutableLiveData` property that represents the **state** of the ViewModel and that’s it. By state I mean, “showing the information”, “loading”, “no internet connection”.

- Sometimes you may need to have more than one `LiveData` property or even use a `MediatorLiveData` property to handle changes within the other properties. For example, let’s say that you have an Activity where you can search for different things using a search bar on the Toolbar. Usually, you will have a `MutableLiveData` property of a String to represent the query of what you are searching for, you can hook this into a `MediatorLiveData` and listen for the changes on the query `LiveData` property, then perform the search accordingly.

- Another example is trying to *handle different error situations*. Let’s say that you have an endpoint that is prone to return a 500 response. If the user opens the app from scratch and gets a 500 when fetching the data you want to show a full display error message (a nice ImageView with a nice title explaining what just happened). On the other hand, if the user opens the app, fetches information successfully, then do a Pull-To-Refresh, and gets a 500, you may want to show just a `Snackbar` instead of the full error display. In a situation like this, you want to keep two different sealed classes to represent the state of the app. One that represents the state of the ViewModel, and the other that represents all the possible error messages. You can see an example of this in the **"LiveData Example"**, there we display a list of posts through a search feature, this search feature also goes with a few filtering options, all this gets coordinated through `LiveData`.

#### Code Example

Taking a look to the **"LiveData Example"** we are going to see that we have two sealed classes to represent the state of the ViewModel, `State` and `Message`. And another sealed class to represent the state of the current search, `SearchParams`.

The `State` class which can be either `IsLoading` (to reflect whether we are currently loading the search results or not), `Success` (to reflect the success state once we have fetched the search results), `NoInternetError` (to reflect the state when there's no internet connection) and `UnknownError` (to reflect the state when something goes wrong while trying to fetch results)

```kotlin
class ExampleImgurViewModel @Inject constructor(
    private val getImgurPostsInteractor: GetImgurPostsInteractor
) : ViewModel(), GetImgurPostsInteractor.Callback {

    // region Sealed classes declaration
    sealed class State {
        data class IsLoading(
            val isLoading: Boolean
        ) : State()
        data class Success(
            val query: String,
            val list: List<ImgurGallery>
        ) : State()
        object NoInternetError : State()
        object UnknownError : State()
    }
    
    // ...
```

The `Message` class represents the **error messages**. As I mentioned before, if we got search results through a search and then we try to search for something else and something unexpected happen (a 500 response from the endpoint, connectivity lost, etc.) we don't want to remove the information that the user is currently seeing and show the full error state, we just want to display a nice friendly `Snackbar`.

```kotlin
    // ...

    sealed class Message {
        object NoInternetError : Message()
        object UnknownError : Message()
    }
    // endregion
```

You can see that on the `GetImgurPostsInteractor.Callback` implementation within the `ExampleImgurViewModel` we handle two error callbacks, one for generic errors and the other for connectivity errors.

```kotlin
    // ...
    
    override fun onErrorFecthingImgurPosts() {
        state.value = State.IsLoading(false)
        if (_imgurGalleryPosts.isEmpty()) {
            state.value = State.UnknownError
        } else {
            message.value = Message.UnknownError
        }
    }

    override fun onNoInternetConnection() {
        state.value = State.IsLoading(false)
        if (_imgurGalleryPosts.isEmpty()) {
            state.value = State.NoInternetError
        } else {
            message.value = Message.NoInternetError
        }
    }
    
    // ...
```

The first thing that we do is to turn off the loading state through the `State.IsLoading(false)`. Then we check against the `_imgurGalleryPosts` property which is a mutable collection of the Imgur posts that we have fetched so far. If the collection is empty, then we know that the user isn't seeing any post-related information, so we go with the full error state. If the collection isn't empty, then we know that the user is seeing posts, and we don't want to hide them, so we go with the message.

```kotlin
class ExampleImgurActivity : AppCompatActivity(), SimpleSearchView.OnQueryTextListener,
    Toolbar.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener {
    
    // ...
    
    // region Lifecycle functions declaration
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        observeStateChanges()
        observeMessageChanges()
        // ...
    }
    
    // ...

    private fun observeMessageChanges() {
        viewModel.message.observe(this, Observer {
            when (it) {
                is ExampleImgurViewModel.Message.NoInternetError -> {
                    onShowNoInternetConnectionSnackbar()
                }
                is ExampleImgurViewModel.Message.UnknownError -> {
                    onShowUnknownErrorSnackbar()
                }
            }
        })
    }

    // ...
    
    private fun onShowUnknownErrorSnackbar() {
        Snackbar.make(
            activityExampleImgurContainer,
            getString(R.string.error_unknown_error),
            Snackbar.LENGTH_LONG
        ).show()
    }

    private fun onShowNoInternetConnectionSnackbar() {
        Snackbar.make(
            activityExampleImgurContainer,
            getString(R.string.error_no_internet_connection),
            Snackbar.LENGTH_LONG
        ).show()
    }
    
    // ...

    private fun observeStateChanges() {
        viewModel.state.observe(this, Observer {
            when (it) {
                is ExampleImgurViewModel.State.IsLoading -> {
                    onLoadingStateChanged(it.isLoading)
                }
                is ExampleImgurViewModel.State.NoInternetError -> {
                    onShowNoInternetConnectionError()
                }
                is ExampleImgurViewModel.State.UnknownError -> {
                    onShownUnknownErrorStateView()
                }
                is ExampleImgurViewModel.State.Success -> {
                    if (it.list.isEmpty()) {
                        onShowNoResultsStateView(it.query)
                    } else {
                        onShowImgurPosts(it.list)
                    }
                }
            }
        })
    }
    
    // ...

    private fun onShownUnknownErrorStateView() {
        activityExampleImgurStateDisplay.show {
            image(R.drawable.ic_sentiment_very_dissatisfied_black_24dp)
            title(R.string.error_unknown_error)
        }
    }

    private fun onShowNoResultsStateView(query: String) {
        activityExampleImgurStateDisplay.show {
            image(R.drawable.ic_youtube_searched_for_black_24dp)
            title(getString(R.string.error_no_search_result_found, query))
        }
    }

    private fun onShowNoInternetConnectionError() {
        activityExampleImgurStateDisplay.show {
            image(R.drawable.ic_signal_wifi_off_black_24dp)
            title(R.string.error_no_internet_connection)
        }
    }

    // ...
```
Here you can see how we observe these changes from the view layer and react accordingly. Keep in mind that in the source code we have a custom view called `SimpleStateDisplay`, which is basically just a `FrameLayout` with an `ImageView` and a `TextView` that I use to display the full error states.

To perform the actual search we have a `MediatorLiveData` property of `SearchParams.Query` called `searchParams`.

```kotlin
class ExampleImgurViewModel @Inject constructor(
    private val getImgurPostsInteractor: GetImgurPostsInteractor
) : ViewModel(), GetImgurPostsInteractor.Callback {
    ...
    sealed class SearchParams {
        enum class Sort(val value: String) {
            TOP("top"),
            VIRAL("viral")
        }
        enum class Window(val value: String) {
            DAY("day"),
            WEEK("week"),
            MONTH("month")
        }
        data class Query(
            val query: String,
            val window: Window,
            val sort: Sort,
            val page: Int
        )
    }
    
    ...
    
    val sort = MutableLiveData<SearchParams.Sort>().apply {
        SearchParams.Sort.TOP // Default sort
    }
    val window = MutableLiveData<SearchParams.Window>().apply {
        SearchParams.Window.MONTH // Default window
    }
    val query = MutableLiveData<String>().apply {
        "" // Default query
    }
    private val page = MutableLiveData<Int>().apply {
        1 // Default page
    }
    
    ...

    // LiveData property that reflects the state of what we are currently searching.
    val searchParams = MediatorLiveData<SearchParams.Query>().apply {
        addSource(sort) { newSort ->
            // Remove all the current posts since the query has changed
            _imgurGalleryPosts.clear()
            value = _searchParams.copy(
                sort = newSort
            )
        }
        addSource(window) { newWindow ->
            // Remove all the current posts since the query has changed
            _imgurGalleryPosts.clear()
            value = _searchParams.copy(
                window = newWindow
            )
        }
        addSource(query) { newQuery ->
            // Remove all the current posts since the query has changed
            _imgurGalleryPosts.clear()
            value = _searchParams.copy(
                query = newQuery
            )
        }
        addSource(page) { newPage ->
            value = _searchParams.copy(
                page = newPage
            )
        }
    }
    
    ...
```
The idea here is that from the View layer we set all the different properties to configure our search criteria. We set the query, the page, the sorting order, the window, etc. The `searchParams` reacts to these changes and updates its state. The `_searchParams` property is a `SearchParam.Query` property that helps us to keep track of the current search criteria, internally, in the ViewModel.

```kotlin
class ExampleImgurActivity : AppCompatActivity(), SimpleSearchView.OnQueryTextListener,
    Toolbar.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener {

    // ...

    private fun observeQueryChanges() {
        viewModel.searchParams.observe(this, Observer {
            viewModel.searchPosts(it)
        })
    }
  
    // ...
```

We observe the `searchParams` changes from the View layer and if we detect that anything has changed on the search criteria, we call the ViewModel and perform a new search against the Imgur API through the `GetImgurPostsInteractor`.

## View

### Protocol

The view protocol is to **expose data** to the **user** through a UI.

### Logic

The logic on the view layer consist of interacting with the ViewModel, observe the changes on the ViewModel through the LiveData properties and update the UI accordingly.

### Code Example

Here you just do the usual set up of the views. For example, setting the listener for a `Button`, preparing the adapters for a `RecyclerView`, or setting up any other view. Other than that the implementation of the view layer should be pretty straightforward. In the `onCreate(Bundle?)` hook we want to inject the dependencies, instantiate the `ViewModel`, set up the views, and listen for the `ViewModel` changes.

```kotlin
class ExampleImgurActivity : AppCompatActivity(), SimpleSearchView.OnQueryTextListener,
    Toolbar.OnMenuItemClickListener, PopupMenu.OnMenuItemClickListener {

    ...

    // region Lifecycle functions declaration
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.example_imgur_activity)

        // Instantiate the ViewModel
        viewInjector.inject(this)

        viewModel = ViewModelProviders.of(this, viewModelProvider)
            .get(ExampleImgurViewModel::class.java)

        // Setup the Views' listeners
        setupSwipeRefreshLayout()
        setupRecyclerView()
        setupToolbar()

        // Observe the changes from the ViewModel
        observeStateChanges()
        observeMessageChanges()
        observeQueryChanges()
        observeQuerySortChanges()
        observeQueryWindowChanges()
        observeSearchQueryChanges()
    }
    ...
```

Another thing that you need to care for is the state of the Fragment/Activity. You can save and restore the state of the view through the `onSavedInstanceState(Bundle)` and `onRestoreInstanceState(Bundle)` hooks.

```kotlin
    ...
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        viewModel.onSaveIntanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        viewModel.onRestoreInstanceState(savedInstanceState)
    }
    ...
```

>What about using Jetpack's SavedStateHandle?

At the time of writing this articles implementing this solution using `SavedStateHandle` requires way more boilerplate code than just hooking the ViewModel from the  `onSavedInstanceState(Bundle)` and `onRestoreInstanceState(Bundle)` hooks. I'll probably update the article as soon as I find a more adequate solution.

# Next

My next article will be about how to integrate Unit Testing (through Robolectric + Square's MockWebServer) and Instrumentation Testing (through Espresso) into this architecture.

# Notes

[1] The DataResponse "pattern" is also used by Google on the [2019 IOSched app](https://github.com/google/iosched/blob/89df01ebc19d9a46495baac4690c2ebfa74946dc/shared/src/main/java/com/google/samples/apps/iosched/shared/result/Result.kt "2019 IOSched app"). It is really useful when you need to do something based on the origin of the data.