Adha_setiawan
Adha_setiawan

Reputation: 27

How to set up Viewmodel class properly?

I'm trying to follow some tutotial from github about MVVM model and i'm stuck at viewmodel class because there's an error says

Not enough information to infer type variable T

and

Type mismatch.
Required:Resource<Movie>
Found:Unit

And when i check my other class like ApiService, Dao, NetworkBoundResource, ApiResponse, Resources and respository everthing fine like this

ApiService :

interface ApiService {
    @GET("3/movie/popular")
    fun getMyMovie(@Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
    ) : Flow<ApiResponse<MovieResponse.Movie>>
}

MovieDao:

@Dao
interface MovieDao : BaseDao<Movie> {

//    @Insert(onConflict = OnConflictStrategy.REPLACE)
//    fun insertMovie(movie: List<Movie>)

    @Query("SELECT * FROM `movie` ORDER by movie_id DESC")
    fun getMyMovie() : Flow<Movie>

    @Query("SELECT * FROM `movie` ORDER by movie_id DESC")
    fun findAllMovie() : Maybe<List<Movie>>

    @Query("SELECT * FROM `movie` ORDER by movie_id DESC")
    fun streamAll() : Flowable<List<Movie>>

    @Query("DELETE FROM `movie`")
    fun deleteAll()
}

MovieRespository:

class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {

    fun getListMovie() : Flow<Resource<Movie>> {
        return networkBoundResource(
                fetchFromLocal = { movieDao.getMyMovie() },
                shouldFetchFromRemote = {true},
                fetchFromRemote = {apiService.getMyMovie()},
                processRemoteResponse = {},
                saveRemoteData = {movieDao.insert(
                        it.results.let {
                            it.map { data -> Movie.from(data) }
                        }
                )},
                onFetchFailed = {_, _ ->}
        ).flowOn(Dispatchers.IO)
    }

NeteorkBoundResource:

inline fun <DB, REMOTE> networkBoundResource(
        crossinline fetchFromLocal: () -> Flow<DB>,
        crossinline shouldFetchFromRemote: (DB?) -> Boolean = { true },
        crossinline fetchFromRemote: () -> Flow<ApiResponse<REMOTE>>,
        crossinline processRemoteResponse: (response: ApiSuccessResponse<REMOTE>) -> Unit = { Unit },
        crossinline saveRemoteData: (REMOTE) -> Unit = { Unit },
        crossinline onFetchFailed: (errorBody: String?, statusCode: Int) -> Unit = { _: String?, _: Int -> Unit }
) = flow<Resource<DB>> {

    emit(Resource.Loading(null))

    val localData = fetchFromLocal().first()

    if (shouldFetchFromRemote(localData)) {

        emit(Resource.Loading(localData))

        fetchFromRemote().collect { apiResponse ->
            when (apiResponse) {
                is ApiSuccessResponse -> {
                    processRemoteResponse(apiResponse)
                    apiResponse.body?.let { saveRemoteData(it) }
                    emitAll(fetchFromLocal().map { dbData ->
                        Resource.Success(dbData)
                    })
                }

                is ApiErrorResponse -> {
                    onFetchFailed(apiResponse.errorMessage, apiResponse.statusCode)
                    emitAll(fetchFromLocal().map {
                        Resource.Error(
                                apiResponse.errorMessage,
                                it
                        )
                    })
                }
            }
        }
    } else {
        emitAll(fetchFromLocal().map { Resource.Success(it) })
    }
}

ApiResponse:

sealed class ApiResponse<T> {
    companion object {

        fun <T> create(error: Throwable): ApiErrorResponse<T> {
            return ApiErrorResponse(
                    error.message ?: "Unknown error",
                    0
            )
        }


        fun <T> create(response: Response<T>): ApiResponse<T> {
            return if (response.isSuccessful) {
                val body = response.body()
                val headers = response.headers()
                if (body == null || response.code() == 204) {
                    ApiEmptyResponse()
                } else {
                    ApiSuccessResponse(
                            body,
                            headers
                    )
                }
            } else {
                val msg = response.errorBody()?.string()
                val errorMsg = if (msg.isNullOrEmpty()) {
                    response.message()
                } else {
                    msg
                }
                ApiErrorResponse(
                        errorMsg ?: "Unknown error",
                        response.code()
                )
            }
        }
    }
}

/**
 * separate class for HTTP 204 responses so that we can make ApiSuccessResponse's body non-null.
 */
class ApiEmptyResponse<T> : ApiResponse<T>()

data class ApiSuccessResponse<T>(
        val body: T?,
        val headers: okhttp3.Headers
) : ApiResponse<T>()

data class ApiErrorResponse<T>(val errorMessage: String, val statusCode: Int) : ApiResponse<T>()

Resource:

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

//    data class Loading<T>(val loadingData: T?) : Resource<T>(Status.LOADING, loadingData, null)
//    data class Success<T>(val successData: T?) : Resource<T>(Status.SUCCESS, successData, null)
//    data class Error<T>(val msg: String, val error: T?) : Resource<T>(Status.ERROR, error, msg)

    companion object {

        fun <T> Success(data: T?): Resource<T> {
            return Resource(Status.SUCCESS, data,null)
        }

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

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

        }
    }
}

MainViewModel:

class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
    @ExperimentalCoroutinesApi
    val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
        when(it.status){
            Resource.Loading() ->{}
            Resource.Success() ->{}
            Resource.Error() ->{}
        }
    }.asLiveData(viewModelScope.coroutineContext)
}

to be specific this what the error looks like and this is the link of tutorial i learn https://github.com/hadiyarajesh/flower

viewModel Class Error

Upvotes: 0

Views: 97

Answers (1)

ferini
ferini

Reputation: 1958

You get the error, because Inside the when, you are trying to construct a new instance of Resource.Loading() etc, but those require a type, so it would need to be something like Resource.Loading<Movie>().

Tht being said, you are doing when(it.status), so the cases in the when, should not be a Resource.Loading, but Status.LOADING instead:

class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
    @ExperimentalCoroutinesApi
    val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
        when(it.status){
            Status.LOADING ->{}
            Status.SUCCESS ->{}
            Status.ERROR ->{}
        }
        return@map it
    }.asLiveData(viewModelScope.coroutineContext)
}

Also, since you are declaring LiveData<Resource<Movie>>, you need to return a Resource<Movie> from the map {} (we could drop return@map, it is just to be explicit)

Upvotes: 1

Related Questions