Progression Partie 8 sur 12

Préparer les données avec un repository

Notre classe CountryApi peut maintenant récupérer les données depuis REST Countries.

Elle retourne cependant un CountriesResponseDto, dont la structure correspond directement à la réponse du serveur.

Le reste de l’application n’a pas besoin de connaître cette structure. Notre interface utilise simplement une liste de Country.

Nous allons donc créer un repository chargé de faire le lien entre l’API et le reste de l’application.

Qu’est-ce qu’un repository ?

Un repository est une classe qui centralise l’accès aux données de l’application.

Dans notre projet, il sera responsable de demander les pays à CountryApi, d’extraire la liste de CountryDto, de transformer chaque DTO en Country, de trier les pays par nom et de retourner le résultat ou l’erreur rencontrée.

L’intérêt de cette séparation est que le ViewModel n’aura pas besoin de connaître la structure JSON ou les DTO.

Il demandera simplement au repository : « Donne-moi la liste des pays. »

Déclarer le repository

Crée le fichier data/CountryRepository.kt.

Dans ce fichier, ajoute :

data/CountryRepository.kt
class CountryRepository(
    private val api: CountryApi
) {
}

Le repository reçoit CountryApi dans son constructeur. Il ne crée pas lui-même l’API ni le client HTTP.

Plus tard, les dépendances seront construites de cette manière :

KOTLIN
val countryApi = CountryApi(
    client = httpClient
)

val countryRepository = CountryRepository(
    api = countryApi
)

Ajouter la fonction getCountries

La fonction retournera une liste de Country.

À l’intérieur de CountryRepository, ajoute :

data/CountryRepository.kt
...
import com.composechef.countryexplorer.model.Country
...

suspend fun getCountries(): Result<List<Country>> {
}

La fonction est marquée suspend parce qu’elle appellera la fonction réseau suspendue de CountryApi.

Son résultat contient une liste de Country. Cette liste peut toutefois ne pas être disponible si une erreur survient.

Nous utilisons donc Result<List<Country>>, qui peut représenter un succès contenant la liste ou un échec contenant une exception.

Capturer les erreurs avec runCatching

Dans le corps de getCountries(), ajoute :

data/CountryRepository.kt
return runCatching {
    api.getCountries()
}

La fonction doit maintenant ressembler à ceci :

data/CountryRepository.kt
suspend fun getCountries(): Result<List<Country>> {
    return runCatching {
        api.getCountries()
    }
}

runCatching exécute le code contenu dans son bloc.

Lorsque le code réussit, il retourne un Result contenant la valeur obtenue. Lorsqu’une exception est déclenchée, il la capture et retourne un échec.

Cela nous évite d’ajouter manuellement un bloc try/catch.

Extraire la liste des DTO

CountryApi.getCountries() retourne un CountriesResponseDto.

Pour atteindre la liste des pays, ajoute .data puis .objects après l’appel à l’API :

data/CountryRepository.kt
api.getCountries()
    .data
    .objects

Le bloc runCatching devient :

data/CountryRepository.kt
return runCatching {
    api.getCountries()
        .data
        .objects
}

Transformer les DTO en Country

Notre interface ne consomme pas des CountryDto, mais des objets Country.

Après .objects, ajoute :

data/CountryRepository.kt
.map(CountryDto::toCountry)

Le code devient :

data/CountryRepository.kt
api.getCountries()
    .data
    .objects
    .map(CountryDto::toCountry)

Pour chaque CountryDto, la fonction toCountry() construit un nouvel objet Country.

Cette écriture revient à écrire :

KOTLIN
.map { countryDto ->
    countryDto.toCountry()
}

Trier les pays par nom

L’API ne garantit pas forcément que les pays seront retournés dans l’ordre souhaité par notre interface.

Après map, ajoute :

data/CountryRepository.kt
.sortedBy(Country::name)

La chaîne complète devient :

data/CountryRepository.kt
api.getCountries()
    .data
    .objects
    .map(CountryDto::toCountry)
    .sortedBy(Country::name)

La liste sera ainsi retournée dans l’ordre alphabétique.

La version finale de CountryRepository

À ce stade, CountryRepository.kt doit contenir :

data/CountryRepository.kt
package com.composechef.countryexplorer.data

import com.composechef.countryexplorer.model.Country

class CountryRepository(
    private val api: CountryApi
) {
    suspend fun getCountries(): Result<List<Country>> {
        return runCatching {
            api.getCountries().data.objects
                .map(CountryDto::toCountry)
                .sortedBy(Country::name)
        }
    }
}

Notre repository est maintenant capable de récupérer, transformer et trier les pays.

Dans la prochaine partie, nous allons représenter les différents états possibles de l’écran avec une interface scellée : chargement, succès et erreur.