Jayesh Rathod
Jayesh Rathod

Reputation: 620

Creating custom network listner for Retrofit Kotlin Coroutine

I am using retofit service without using callback. So, its probably going with Kotlin Coroutine suspend fun. I referred many blogs, mediums and so many tutorials. Well this is easy to get response using coroutine scope with IO and Main thread.

So, after referring some examples I am considers to do code like this:

Restrofit Service Interface RetrofitInterfaces.kt:

interface RetrofitInterfaces {
    @FormUrlEncoded
    @POST("get-videos-pagination")
    suspend fun getTemplates(@Field("app_name") app_name: String,
                             @Field("sort_by") sortBy: String,
                             @Field("video_loaded_ids") loadedIds: String): Response<Model_Video_List>
}

Where Model_Video_List.kt class is my response data class.

Retrofit Builder RetrofitClient.kt:

object RetrofitClient {
    fun makeRetrofitService(mContext: Context): RetrofitInterfaces {
    val gson = GsonBuilder().setLenient().create()
    return Retrofit.Builder()
            .baseUrl(AppPreferences.getBaseUrl(mContext) + "/api/v3/")
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build().create(RetrofitInterfaces::class.java)
}
}

Here I am using Gson to serialise or deserialise Json data.

Now what is problematic and where problem started in to get error response I am using ResultWrapper.kt:

class ResultWrapper {
    enum class ErrorCode {
        NETWORK,// Connection failed or timeout
        SERVER,// Server errors due to maintenance or over request
        INVALID// Response is success but can't serialised with Data Class!
    }

    interface ResponseWrapper {
        suspend fun onSuccess(response: Response<Any>)

        suspend fun onFailure(eCode: ErrorCode)
    }

    private var responseWrapper: ResponseWrapper? = null
        set

    suspend fun enqueue(response: Response<Any>, responseWrapper: ResponseWrapper) {
        this.responseWrapper = responseWrapper
        if (response.isSuccessful) {
            responseWrapper.onSuccess(response)
        } else {
            val errorCode = response.code()
            when (errorCode) {
                in 200..299 -> {
                    responseWrapper.onFailure(ErrorCode.INVALID)
                }
                in 400..499 -> {
                    responseWrapper.onFailure(ErrorCode.NETWORK)
                }
                in 500..599 -> {
                    responseWrapper.onFailure(ErrorCode.SERVER)
                }
            }
        }
    }
}

So, now I can call this enqueue fun from anywhere in app like:

private fun getVideoList(loadedIds: String, sortBy: String) {
    val service = RetrofitClient.makeRetrofitService(mContext)
    scope.launch {
        val response = service.getTemplates("MyApp", sortBy, loadedIds)
        val resultWrapper = ResultWrapper()
        resultWrapper.enqueue(response, object : ResultWrapper.ResponseWrapper {
            override suspend fun onSuccess(response: Response<Any>) {

            }

            override suspend fun onFailure(eCode: ResultWrapper.ErrorCode) {

            }
        })
    }
}

But I am getting compile error for passing response in resultWrapper.enqueue() because in ResultWrapper.ResponseWrapper interface there is method defined with suspend fun onSuccess(response: Response<Any>). So, type conversion problem occurred here!

If Retrofit CallBack<T> can handle any type of Model or Data classes and return which is exact tpyed. Like above service requires Model_Video_List class in response and other services requires there own then what to do for it? Is there only way to use separate function for every service? Or is it only possible in Java?!

Upvotes: 1

Views: 953

Answers (1)

Saurabh Thorat
Saurabh Thorat

Reputation: 20684

You can introduce a generic parameter type T in your class ResultWrapper, so whenever you create an instance of this class you must specify an actual type in place of T.

Change your ResultWrapper to this:

class ResultWrapper<T> {
    enum class ErrorCode {
        NETWORK,// Connection failed or timeout
        SERVER,// Server errors due to maintenance or over request
        INVALID// Response is success but can't serialised with Data Class!
    }

    interface ResponseWrapper<T> {
        suspend fun onSuccess(response: Response<T>)

        suspend fun onFailure(eCode: ErrorCode)
    }

    private var responseWrapper: ResponseWrapper<T>? = null
        set

    suspend fun enqueue(response: Response<T>, responseWrapper: ResponseWrapper<T>) {
        this.responseWrapper = responseWrapper
        if (response.isSuccessful) {
            responseWrapper.onSuccess(response)
        } else {
            val errorCode = response.code()
            when (errorCode) {
                in 200..299 -> {
                    responseWrapper.onFailure(ErrorCode.INVALID)
                }
                in 400..499 -> {
                    responseWrapper.onFailure(ErrorCode.NETWORK)
                }
                in 500..599 -> {
                    responseWrapper.onFailure(ErrorCode.SERVER)
                }
            }
        }
    }
}

And also change your method to this:

private fun getVideoList(loadedIds: String, sortBy: String) {
    val service = RetrofitClient.makeRetrofitService(mContext)
    scope.launch {
        val response = service.getTemplates("MyApp", sortBy, loadedIds)
        val resultWrapper = ResultWrapper<Model_Video_List>()
        resultWrapper.enqueue(response, object : ResultWrapper.ResponseWrapper<Model_Video_List> {
            override suspend fun onSuccess(response: Response<Model_Video_List>) {

            }

            override suspend fun onFailure(eCode: ResultWrapper.ErrorCode) {

            }
        })
    }
}

Upvotes: 5

Related Questions