ViewModel en Android
Tags: android, architecture
Published: 2023-09-15 08:32:07

## Índice

- [Qué es](#qué-es)
- [Para qué sirve](#para-qué-sirve)
- [Beneficios de uso](#beneficios-de-uso)
- [Ciclo de vida](#ciclo-de-vida)
- [Implementación básica](#implementación-básica)
  - [Dependencias](#dependencias)
  - [Clase ViewModel](#clase-viewmodel)
  - [Composable](#composable)
- [Implementación con DI](#implementación-con-di)
- [ViewModel Cheatsheet](#viewmodel-cheatsheet)
- [Referencias](#referencias)

-----

## Qué es
El `ViewModel` en Android forma parte del patrón arquitectónico `Model-View-ViewModel`, siendo éste el ultimo de dicho patrón.

Es decir que es una forma de organizar el código para que sea mas entendible, mas estructurado y mas testable.

## Para qué sirve
La idea fundamental es tener la parte lógica y las variables fuera de la vista *(fuera del codigo de jetpack compose por ejemplo)*. 

Por ejemplo si vamos a hacer una petición a una API HTTP u obtener datos de una base de datos, esta funcionalidad deberíamos realizarla desde el ViewModel. De esta manera conseguimos que en la vista solo se rendericen datos para que el usuario los vea, **en ningún momento tenemos lógica**.

> El **ViewModel** **no debe interactuar** directamente con una **API** o una **base de datos**, utilizaremos el patrón **Repository**. Solo debe utilizar el repositorio, obtener los datos en "crudo" y realizar las modificaciones oportunas para enviarlas a la view.

## Beneficios de uso
Las ventajas principales de utilizar `ViewModel` en Android son:

- El ciclo de vida del ViewModel tiene mas alcance que un `Activity`
	
> Esto nos permite almacenar datos *(variables)* y si hay un cambio en el dispositivo, como rotación de pantalla, los datos seguirán presentes en el ViewModel. Conserva el estado de las vistas.

- Nos proporciona acceso a la lógica empresarial

## Ciclo de vida
El `ViewModel` no consta de un ciclo de vida tradicional como el que encontramos en las `Activity` o `Fragment`. En su lugar, tiene un alcance específico dentro del ciclo de vida, y este alcance es más extenso.

A diferencia del ciclo de vida de una `Activity` o `Fragment`, el `ViewModel` ofrece un alcance más amplio. Esta particularidad nos permite conservar datos en el `ViewModel` a pesar de que ocurran cambios en la interfaz, como rotaciones de pantalla o si la `Activity` pasa por cambios de estado, entre otros eventos.

La siguiente imagen muestra el flujo de una `Activity` y los métodos que se activan en respuesta a diversos eventos. A la derecha, se puede observar el amplio alcance del `ViewModel`.

![Alcance del ViewModel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/131xn1wyshmhewqeel46.png)


## Implementación básica
En este ejemplo voy a mostrar como implementar el `ViewModel` junto a `Jetpack Compose` de forma básica. 

También haré uso de elementos como `MutableLiveData` y `LiveData` *(son implementaciones para clases observables, es decir que nos subscribimos a la clase y si ocurre un cambio, reaccionamos haciendo lo que sea)* 

### Dependencias
> [Link oficial](https://developer.android.com/jetpack/androidx/releases/lifecycle?hl=es-419#kts)

####  app/build.gradle.kts
```kotlin
dependencies {
	...
	val lifecycle_version = "2.6.1"  
	implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
}
```

#### Clase ViewModel
```kotlin
class GreetingViewModel : ViewModel() {  
    /*  
     * Variable (_randomvm) que observamos y podemos modificar, se usa a nivel interno en     * los ViewModels     */  
    private val _randomvm: MutableLiveData<Int> = MutableLiveData<Int>(0)  
    val randomvm: LiveData<Int> = _randomvm  
  
    init {  
        _randomvm.value = Random.nextInt(0, 2023)  
    }  
}
```

En el `ViewModel` hemos creado dos variables, ambas son clases observables. La única diferencia es: 

- `MutableLiveData` nos permite modificar el valor que almacena. 
- `LiveData` no nos permite modificar el valor almacenado.

> Siempre nos interesa que solo el `ViewModel` modifique los valores de las variables observables. Por este motivo utilizamos `MutableLiveData` de forma privada y `LiveData` de forma pública. Así solo se cambia el contenido dentro del `ViewModel`.

Al instanciarse la clase `GreetingViewModel` se ejecutará el método `init`. Éste método genera un número aleatorio y modifica el valor del `MutableLiveData`. `randomvm` al ser igual que `MutableLiveData`, también cambiará su valor, y allá donde se observe la variable, se reaccionará a los cambios.

#### Composable 
```kotlin
@Composable  
fun Greeting(  
    name: String,  
    modifier: Modifier = Modifier,  
    viewModel: GreetingViewModel = viewModel()  
) {  
    val randomFromVM = viewModel.randomvm  
    val random: Int = Random.nextInt(0, 2023)  
  
    Log.d("Random", "From ViewModel: ${randomFromVM.value}")  
    Log.d("Random", "From local composable's scope: $random")  
  
    Text(  
        text = "Hello $name!",  
        modifier = modifier  
    )  
}
```

En el composable instanciamos el viewModel utilizando el metodo `viewModel()` *(que se encuentra en las dependencias instaladas en el primer paso)* como argumento de la función, y dentro del composable ya podemos utilizar los métodos y atributos públicos.

Las variables que tenemos son:
- `randomFromVM`: es lo mismo que la variable `randomvm` del viewmodel
- `random`: una variable en el ámbito local de la función.

Si ejecutamos el programa, vemos que cada vez que rotemos el dispositivo el valor de `random` será diferente, y el valor de `randomFromVM` será siempre el mismo, ya que este dato se almacena en el viewmodel y este tiene un mayor alcance.

![Salida logcat](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7363qiwn0stlim1nz8ip.png)


## Implementación con DI
Al utilizar inyección de dependencias con Hilt la implementación de un viewmodel es diferente, ya que deberemos proveer el ViewModel a hilt y después utilizar un método propio de hilt.

La única diferencia es que anotamos la clase con `@HiltViewModel` y en el `@Composable` lo inyectamos mediante el método `hiltViewModel()`.


#### Clase ViewModel

```kotlin
@HiltViewModel  
class GreetingViewModel @Inject constructor() : ViewModel() {  
	private val _randomvm: MutableLiveData<Int> = MutableLiveData<Int>(0)  
    val randomvm: LiveData<Int> = _randomvm  
  
    init {  
        _randomvm.value = Random.nextInt(0, 2023)  
    }  
}

```

#### Composable

```kotlin
@Composable  
fun Greeting(  
    name: String,  
    modifier: Modifier = Modifier,  
    viewModel: GreetingViewModel = hiltViewModel()  
) {  
    val randomFromVM = viewModel.randomvm  
    val random: Int = Random.nextInt(0, 2023)  
  
    Log.d("Random", "From ViewModel: ${randomFromVM.value}")  
    Log.d("Random", "From local composable's scope: $random")  
  
    Text(  
        text = "Hello $name!",  
        modifier = modifier  
    )  
}
```

> Para implementar el viewModel junto a Hilt es necesario agregar nuevas dependencias en gradle, yo en el ejemplo no lo he hecho, pero la documentación oficial es la siguiente: [Androidx Releases](https://developer.android.com/jetpack/androidx/releases/hilt?hl=es-419#hilt-navigation-compose_version_100_2)

## ViewModel Cheatsheet
Adjunto una imagen de un cheat sheet de la documentación oficial de Android, dejo el [enlace al pdf web](https://developer.android.com/images/topic/libraries/architecture/viewmodel-apis-cheatsheet.pdf).


![Viewmodel Cheatsheet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0sf0yg8lmp7fxbbzwddc.png)


## Referencias

{% embed https://developer.android.com/topic/libraries/architecture/viewmodel?hl=es-419 %}