Using ViewModel-LiveData with Jetpack Compose
Tags: android, compose
Published: 2022-09-12 12:20:31

---
title: Using ViewModel-LiveData with Jetpack Compose
published: true
description: Where and how to make API calls from composable function
tags: Android, Compose
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
---

In this post I want to cover on where/how to make API calls on a Jetpack compose screen. In an essence the traditional UI system and and compose differs on where do we invoke the remote/async API vs how the data delivered to us. Following swim-lane diagram explains the overview of the data flow.


![Compose-ViewModel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7z7tzyi4025vccjqqfm.png)

As we can see in the diagram, the ViewModel and inner layers don't differ. In other words, if  you're using ViewModel with Android UI, only the UI classes will change and rest of the layers can be kept as it is.

 ---

## Implementation

In compose, LiveData is consumed as state. To do so, add this dependency in `build.gradle`. 

```groovy
// https://maven.google.com/web/index.html?q=livedata#androidx.compose.runtime:runtime-livedata
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
```
### One-off call

In the composable function, observe the data as state using `LiveData#observeAsState` extension. 

Next, make API call using `LaunchedEffect` - for one time call, use `Unit` or any constant as key.

For UI, as usual - skim through the data and construct the UI. This example shows listing books. 

```kotlin
@Composable
fun BooksScreen(
    viewModel: BookListViewModel = hiltViewModel<BookListViewModelImpl>()
) {
    // State
    val books = viewModel.books.observeAsState()

    // API call
    LaunchedEffect(key1 = Unit) {
        viewModel.fetchBooks()
    }

    // UI
    LazyColumn(modifier = modifier) {
        items(books) {
            // List item composable
            BookListItem(book = it)
        }
    }
}
```

### User triggered - API calls

In case you want to execute the LaunchedEffect block again - such as force refresh, use a variable and conditionally update the `key1` value. Remember every time when the key change, it'll invoke the API. So, keep in mind to not assign the value in render logic and put it behind user action.

```kotlin
@Composable
fun BooksScreen(
    viewModel: BookListViewModel = hiltViewModel<BookListViewModelImpl>()
) {
    // State
    val books = viewModel.books.observeAsState()
    var refreshCount by remember { mutableStateOf(1) }

    // API call
    LaunchedEffect(key1 = refreshCount) {
        viewModel.fetchBooks()
    }

    // UI
    Column() {
        IconButton(onClick = {
                        refreshCounter++
                   }) {
                        Icon(Icons.Outlined.Refresh, "Refresh")
                   }
        LazyColumn(modifier = modifier) {
            items(books) {
                // List item composable
                BookListItem(book = it)
            }
        }
    }
}
```

### ViewModel implementation
ViewModel is an interface contract which exposes data through LiveData and has helper functions to carry out actions.
```kotlin
interface BookListViewModel {
    // Data
    val books: LiveData<List<Book>>
    // Operations
    fun fetchBooks()
}
```
The consuming classes shall refer the interface and the actual implementation will be an Android ViewModel. Wiring of this implementation to UI classes will be taken care by dependency injection. 

Internally, the viewmodel implementation overrides the data variables to provide actual data. As for the operations, the `viewModelScope` ensures the API call lives within viewmodel's lifetime, and launches the remote operation.

> Remember viewModelScope still executes in Main thread. Offloading the task to IO happens in repository layer.

```kotlin
class BookListViewModelImpl(private val repo: BooksRepository) : BookListViewModel {
    private val _books = MutableLiveData<List<Book>>()
    override val books: LiveData<List<Book>>
        get() = _books
      
    override fun fetchBooks() {
        viewModelScope.launch {
            _books.value = repo.fetchBooks()
        }
    }
}
```

### Repo implementation
Repo executes a long running operation. In kotlin world, it is a suspend function runs in IO dispatcher context.

```kotlin
class BooksRepository {

    suspend fun fetchBooks() : List<Book> = withContext(Dispatchers.IO) {
        // Some API call
        // Parser logic
        val books = listOf<Book>()
        books
    }
}
```