Table of contents
- Read First
- My workflow
- Use Postman to query endpoint
- Model response object
- Create method in interface
- Create method in Repository
The code
YouTube version
Introduction
- I have embarked on my next app, a Twitch client app. This series will be all my notes and problems faced when creating this app.
READ FIRST
- This tutorial already assumes you are familiar with Retrofit and have retrofit up and running in your app. This tutorial is meant to be a more abstract guide and give individuals a broad understanding of my work flow. Don't focus too much on the details, it is the series of steps within this workflow that are most helpful
Workflow when using Retrofit
- As I have been working with the Twitch API, I have been dealing a lot with Retrofit. So much so that I have developed a little workflow for when I need to call a new endpoint of the API. The workflow consists of 4 steps:
1) Use Postman to query endpoint
2) Model the response object
3) Create method in Retrofit interface
4) Create method in the Repository layer
1) Use Postman to query the endpoint
- Documentation can only tell us so much and sometimes it can be out of date or incorrect. That is why before I start coding I like to query the endpoint with Postman. This has two main benefits:
1) Verifies that we are hitting the proper endpoint with all the proper headers and parameters.
2) Gives us a proper example of what the response object will be. It is also important that we verify that the response object is in JSON. JSON is the proper format that Retrofit will use to convert to our Kotlin object
2) Model the response object
- So lets assume we are using the get-streams API endpoint. Upon a successful response, we will get a JSON object that looks something like this:
"data": [
{
"id": "40457151671",
"user_id": "582765062",
"user_login": "premiertwo",
"user_name": "PremierTwo",
"game_id": "33214",
"game_name": "Fortnite",
"type": "live",
"title": "fork knife @rubberross @ironmouse @aicandii | Tokyo, Japan | !discord",
"viewer_count": 3506,
"started_at": "2023-07-17T08:14:13Z",
"language": "en",
"thumbnail_url": "https://static-cdn.jtvnw.net/previews-ttv/live_user_premiertwo-{width}x{height}.jpg",
"tag_ids": [],
"tags": [
"Improv",
"Forehead",
"AMA",
"VoiceActing",
"Japan",
"Travel",
"English",
"NoSpoilers"
],
"is_mature": false
},
]
- Our modeled response object will then look like this:
data class FollowedLiveStreams(
val data:List<StreamData>
)
data class StreamData(
val id:String,
@SerializedName("user_id")
val userId:String,
@SerializedName("user_login")
val userLogin:String,
@SerializedName("user_name")
val userName:String,
@SerializedName("game_id")
val gameId:String,
@SerializedName("game_name")
val gameName:String,
val type:String,
val title:String,
@SerializedName("viewer_count")
val viewerCount:Int,
@SerializedName("started_at")
val startedAt:String,
val language:String,
@SerializedName("thumbnail_url")
val thumbNailUrl:String,
@SerializedName("tag_ids")
val tagIds:List<String>,
val tags:List<String>,
@SerializedName("is_mature")
val isMature:Boolean
)
- There are 2 things you should look out for when trying to model a JSON response object:
1) [] square brackets : indicate a list. That is why the Kotlin data class version of "tags": ["Improv"]
is val tags:List<String>
2) {} curly brackets : indicates a new object. Which is why we are modeling the JSON response object as two classes. Notice how its "data": [ {
, the square brackets followed by the curly brackets are telling us that data
is a list of objects. Which results in our data:List<StreamData>
value
3) Create method in Retrofit interface
- Now is the part where we modify the interface which will represent out Http call. Assuming we are still using the get-streams API, the call would look like this:
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
@GET("streams/followed")
suspend fun getFollowedStreams(
@Header("Authorization") authorization:String,
@Header("Client-Id") clientId:String,
@Query("user_id") userId:String
): Response<FollowedLiveStreams>
- Notice how we can use
@Header
,@Query
to define dynamic values for the query parameter and the header. Also, look how the return object isResponse<FollowedLiveStreams>
, we are using theFollowedLiveStreams
we have created previously. Retrofit will automatically map the JSON object to ourFollowedLiveStreams
object
4) Create method in the Repository layer
- Now inside of the repository layer we can make the actual call the the api, like so:
class TwitchRepoImpl(
private val twitchClient: TwitchClient = TwitchRetrofitInstance.api
) {
suspend fun getFollowedLiveStreams(
authorizationToken: String,
clientId: String,
userId: String
): Flow<Response<FollowedLiveStreams>> = flow{
emit(Response.Loading)
val response = twitchClient.getFollowedStreams(
authorization = authorizationToken,
clientId = clientId,
userId = userId
)
if (response.isSuccessful){
emit(response.body())
}else{
emit(Response.Failure(Exception("Error!, code {${response.code()}}")))
}
}
}
- The code above might seem a little confusing but to clarify things, the Response object is of my own creation:
sealed class Response<out T> {
object Loading: Response<Nothing>()
data class Success<out T>(
val data:T
):Response<T>()
data class Failure(
val e:Exception
):Response<Nothing>()
}
I am returning Kotlin Flows from
getFollowedLiveStreams()
so my downstream classes (ViewModels) are able to callcollect{}
on this method and react differently depending if the Response is Loading, Success or FailureAfter we have defined the method inside of the repository layer, we are free to use the method as we see fit.
Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on Twitter.
Top comments (0)