0

I've following class to map request status (Loading, Success, Error)

data class Resource<out T>(
    val status: Status,
    val data: T?
) {
    sealed class Status {
        data class Success<out T>(val data: T?) : Status()

        data class Error(
            val message: String,
            val statusCode: Int
        ) : Status()

        object Loading : Status()
    }

    companion object {
        fun <T> success(data: T?): Resource<T> {
            return Resource(status = Status.Success(data), data = data)
        }

        fun <T> error(msg: String, statusCode: Int, data: T? = null): Resource<T> {
            return Resource(status = Status.Error(msg, statusCode), data = data)
        }

        fun <T> loading(data: T? = null): Resource<T> {
            return Resource(status = Status.Loading, data = data)
        }
    }
}

I use following class to map the request

inline fun <REMOTE> networkResource(
    crossinline fetchFromRemote: suspend () -> Flow<ApiResponse<REMOTE>>,
    crossinline onFetchFailed: (errorBody: String?, statusCode: Int) -> Unit = { _: String?, _: Int -> }
) = flow<Resource<REMOTE>> {
    emit(Resource.loading(null))

    fetchFromRemote().collect { apiResponse ->
        when (apiResponse) {
            is ApiSuccessResponse -> {
                apiResponse.body?.let {
                    emit(Resource.success(data = it))
                }
            }
            is ApiErrorResponse -> {
                onFetchFailed(apiResponse.errorMessage, apiResponse.statusCode)
                emit(Resource.error(msg = apiResponse.errorMessage, statusCode = apiResponse.statusCode, data = null))
            }
            is ApiEmptyResponse -> {
                emit(Resource.success(data = null))
            }
        }
    }
}

I use that class as below

fun getSomething(): Flow<Resource<SuccessResponse<GetCartResponse>>> {
        return networkResource(
            fetchFromRemote = { cartApi.getCart() },
            onFetchFailed = { _, _ -> }
        ).flowOn(Dispatchers.IO)
    }

When i try to map that flow, I'm seeing unchecked cast issue. I've tried to play with reified type but found no solution.

getSomething().collect { response ->
    when (response.status) {
        is Resource.Status.Loading -> {
            Resource.loading(null)
        }
        is Resource.Status.Success<*> -> {
            // Below line states that Unchecked cast: Resource.Status to Resource.Status.Success<Quote>
            val quote = it.status as Resource.Status.Success<Quote>
            Resource.success(quote.data)
        }
        is Resource.Status.Error -> {
            val error = it.status as Resource.Status.Error
            Resource.error(error.message, error.statusCode)
        }
    }
}

How should i make sure that i have access to type T correctly? For more details, please see this

Annon
  • 633
  • 7
  • 26
  • You're using `*` as type argument in the `when`, have you using the actual type? `is Resource.Status.Success` – gpunto Jul 11 '22 at 17:40
  • That also did not worked. That's why used * – Annon Jul 11 '22 at 18:44
  • Ok, I didn't see that the `Status` class was not parameterized based on `Resource`'s `T`. Where is the `Quote` type argument coming from? In the code you posted, it only appears inside `collect`. How do you know that status is a `Success`? And what about `SuccessResponse` and `GetCartResponse`? If you can, please post a minimal, but complete, reproducible example. – gpunto Jul 11 '22 at 22:28
  • Did you know you don't have to call your generics with just letters like `T` or `R` or whatever? To me is clearer calling them like `ResourceDataType` and `StatusDataType`. It's good practice: https://stackoverflow.com/a/30146204/1372195 – Carlo Moretti Jul 12 '22 at 09:31

0 Answers0