Lesson 06 Network Calls ‐ Calling API endpoints with Retrofit - FranGarc/LearningPath GitHub Wiki
Topic: Retrofit: Calling API endpoints.
When using an API Rest, the most popular library to do so is Retrofit. It takes care of the http communication, if well it needs to be used inside asynchronous scopes such as viewModelScope.
Task: Implement API communication with Retrofit.
If you check the response of the two API endpoints we'll be using pokemonList and pokemonDetail you'll notice their data model is quite different from the one we're using. You'll get over 50 new classes. Now you'll need to
- Move current data stuff (both the DataSource and the model package) into /data/mock/
- create the /data/remote/model package to put the new data classes in it.
- create the new DTO objects from the JSON (using JSON 2 kotlin plugin) that actually comes from the API. We won't be needing all of the new classes, so study the json which ones will contain the information we need to populate/instantiate our domain objects. Remember to rename the ones we'll be using so they end in "Dto". Remember to change the "Any?" types for the actual nullable type (i.e. String?, Int?, etc) you may be getting.
- change the mapper methods in the Domain layer so that the Entity get the informaton from the new DTO classes.
Once that's done, you can start with the Retrofit part - you needed the data classes to be able to define their returns.
Also, you'll need a new, non-mock DataSource, call it RemoteDataSource, and make it implement our existing DataSourceAdapter. The methods will call the Retrofit methods.
Requisites
-
Check the Retrofit github for the latest dependency version to add to your app.
-
Add the retrofit's gson converter and google's gson latest dependencies
-
Refactor /data/ contents (DataSource and /model/ package) to /data/mock/
-
Create /data/remote/model package
-
Get the json from the responses to the API endpoints and use them to create the data classes using Json to Kotlin plugin (check tip). Add them to /data/remote/model
-
Add the dependencies for retrofit, and okhttp3's okhttp, and logging-interceptor
-
Create in /data/remote/ the Interface PokeApi with method signatures
- To get the list of pokemon (endpoint "pokemon"), returns a Call. Give default values to parameters limit (150), and offset (0). This way, you can just call this endpoint without additional parameters for the moment.
- To get the pokemon details (endpoint "pokemon/{name}"), returns a Call
- Because we're using Retrofit's Call, these methods are not suspend functions, but regular ones.
-
Create in /data/remote/ an Object RetrofitClient using the pokemon api details (baseUrl) and the interface created in the previous point. You'll also add to it a gsonconverter, and an okHttpClient. The okHttpClient will have an Interceptor HttpLoggingInterceptor with logging level at BODY.
-
Create in /data/remote/ an Interface PokemonService that will define the methods to get the List and the Detail. They will return a Flow
-
Implement in /data/remote/ the PokemonService interface in PokemonServiceImpl using here the retrofit object. For the moment being, just use call.execute().body() to access to the data we want to return, but bear in mind that Calls can be successful or not, therefore the return type will be nullable. We'll deal with error management in a later lesson.
-
Create in /data/remote/ a new RemoteDataSource that implements the DataSourceAdapter. It will use the PokemonService to get the data.
-
Replace the MockDataSource for the RemoteDataSource in the UseCase classes.
-
Make sure you're using Dispatchers.IO in the viewModelScope when calling these UseCases, or you'll get a crash when running the app.
If everything went Ok, you should be able to run the app and see all the 150 pokemon of the first generation.
Tips & Advice.
When creating the data classes for the DTO using the Json to Kotlin plugin, make sure to press Advanced, and do the following:
- on the Property tab, under Type, choose _Auto Determine Nullable Or Not Null fron JSON Value. You'll still need to doublecheck for "Any?" and change that for the corresponding type.
- on the Annotation tab, choose Gson annotation. This avoids the need for you to manually reformat the field names.
You'll get something similar to
data class Sprites(
@SerializedName("back_default")
val backDefault: String,
@SerializedName("back_female")
val backFemale: Any?,
@SerializedName("back_shiny")
val backShiny: String,
@SerializedName("back_shiny_female")
val backShinyFemale: Any?,
@SerializedName("front_default")
val frontDefault: String,
@SerializedName("front_female")
val frontFemale: Any?,
@SerializedName("front_shiny")
val frontShiny: String,
@SerializedName("front_shiny_female")
val frontShinyFemale: Any?,
@SerializedName("other")
val other: Other,
@SerializedName("versions")
val versions: Versions
)
You'll need to change the "Any?" for the corresponding nullable type (in all the cases of this particular class: "String?"). Do this only on the classes you'll be actually using, since there are over 50 classes in the pokeapi data model and we won't be using more than 10.
Research terms.
- Retrofit
- Retrofit.Builder
- Retrofit Call: execute, enqueue, cancel
- GsonConverterFactory
- OkHttp
Additional resources.
Endpoints:
- List of pokemon: https://pokeapi.co/api/v2/pokemon?limit=100&offset=0
- Details of a given pokemon: https://pokeapi.co/api/v2/pokemon/ditto