Saltar a contenido

8.3 Datos de internet con Retrofit

La mayoría de apps consume una API REST: una dirección web que devuelve datos en formato JSON. En Android se usa Retrofit para pedirlos y convertirlos en objetos Kotlin.

Preparación

implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")

Y el permiso INTERNET en el manifest (igual que con las imágenes, 4.3).

Los tres elementos

1. Una clase que representa el JSON

data class Publicacion(val id: Int, val title: String, val body: String)

2. Una interfaz con las peticiones

interface ApiService {
    @GET("posts")
    suspend fun publicaciones(): List<Publicacion>

    @GET("posts/{id}")
    suspend fun publicacion(@Path("id") id: Int): Publicacion
}

3. El objeto Retrofit

object Api {
    val servicio: ApiService = Retrofit.Builder()
        .baseUrl("https://jsonplaceholder.typicode.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
        .create(ApiService::class.java)
}

La dirección de ejemplo es una API pública de pruebas.

Usarlo desde el ViewModel

class PostsViewModel : ViewModel() {
    private val _estado = MutableStateFlow<Estado>(Estado.Cargando)
    val estado = _estado.asStateFlow()

    init { cargar() }

    fun cargar() = viewModelScope.launch {
        _estado.value = Estado.Cargando
        _estado.value = try {
            Estado.Exito(Api.servicio.publicaciones())
        } catch (e: Exception) {
            Estado.Error("No se pudo cargar")
        }
    }
}

sealed interface Estado {
    data object Cargando : Estado
    data class Exito(val datos: List<Publicacion>) : Estado
    data class Error(val mensaje: String) : Estado
}

Y la pantalla reacciona a cada estado con when (el patrón cargando / error / datos de la 7.4):

val estado by vm.estado.collectAsStateWithLifecycle()
when (val e = estado) {
    Estado.Cargando -> CircularProgressIndicator()
    is Estado.Error -> Column { Text(e.mensaje); Button(onClick = vm::cargar) { Text("Reintentar") } }
    is Estado.Exito -> LazyColumn { items(e.datos) { Text(it.title, Modifier.padding(12.dp)) } }
}

Nunca en el hilo principal

Las peticiones de red tardan. Por eso las funciones de Retrofit son suspend y se llaman desde una corrutina (viewModelScope.launch). Si bloqueas el hilo principal, la app se congela.