Teo
Teo

Reputation: 982

Kotlin Flow - Generic function for Retrofit with Result sealed class

I am recently working with Flow in my retrofit's repository.

Sealed class for Result

enum class ApiStatus{
    SUCCESS,
    ERROR,
    LOADING
}

sealed class ApiResult <out T> (val status: ApiStatus, val data: T?, val message:String?) {

    data class Success<out R>(val _data: R?): ApiResult<R>(
        status = ApiStatus.SUCCESS,
        data = _data,
        message = null
    )

    data class Error(val exception: String): ApiResult<Nothing>(
        status = ApiStatus.ERROR,
        data = null,
        message = exception
    )

    data class Loading<out R>(val _data: R?, val isLoading: Boolean): ApiResult<R>(
        status = ApiStatus.LOADING,
        data = _data,
        message = null
    )
}

Example repository call for 3 state - Loading, Error, Success

    fun googleDisconnect() = flow {
        emit(ApiResult.Loading(null, true))  

        val call = userDataSource.self("v4").googleDisconnect()

        if(call.isSuccessful) {
            emit(ApiResult.Success(call.body()))
        } else {
            emit(ApiResult.Error("Google Disconnect Failed"))
        }
    }

However, I have multiple network call with different function in my repository. Is there any idea to write a generic function for these flow so that these flow can be emitted to the flow builder?

My attempt but problem is How can I pass suspend function into the function? enter image description here

enter image description here

Upvotes: 5

Views: 3091

Answers (1)

Teo
Teo

Reputation: 982

Finally I got myself the answer. I wonder if this will helps but I will post out my answer.

fun <T> toResultFlow(call: suspend () -> Response<T>?) : Flow<ApiResult<T>?> {
    return flow {
        emit(ApiResult.Loading())

        val c = call()  <-- have to initialize the call method first
        c?.let {
            try{
                if(c.isSuccessful) {
                    c.body()?.let {
                        emit(ApiResult.Success(it))
                    }
                } else {
                    c.errorBody()?.let {
                        val error = it.string()
                        it.close()
                        emit(ApiResult.Error(error))
                    }
                }
            }catch (e: Exception) {
                emit(ApiResult.Error(e.toString()))
            }
        }

    }.flowOn(Dispatchers.IO)
}

Then, pass in your suspend function as lambda

fun googleDisconnect() = toResultFlow {
    userDataSource.self("v4").googleDisconnect()
}  

Finally, the toResultFlow will be return Flow<ApiResult> and T is your preferred datatype! Volla!

Upvotes: 6

Related Questions