Search code examples
androidkotlinretrofit

Kotlin handle retrofit request with coroutines


I'm making an Android app and i'm trying to make a login. I made a basic retofit request and it works, but i want to handle the response from the server with a generic class, to show error to the user (for example email/password wrong). I follow this tutorial https://blog.mindorks.com/using-retrofit-with-kotlin-coroutines-in-android but here he make the request in the viewModel and access to the data stored in Resource in the mainActivity (the view class). I want to access data in the viewModel to save some information in shared preferences (look the comment in the first code block), but I don't know how to do this. Can someone explain me how to change the code to have access to data in Resource from the ViewModel? Here is my viewModel:

class LoginViewModel(private val loginRepo: LoginRepository) : ViewModel() {
private fun makeLogin(email: String, password: String) {
        viewModelScope.launch {
            Resource.loading(data = null)
            try {

                val usr = User(email, password)
                Resource.success(data = loginRepo.makeLogin(usr))
                // HERE I WANT TO ACCESS TO DATA TO STORE SOME INFORMATION IN SHARED PREFERENCES
            } catch (ex: Exception) {
                Resource.error(data = null, message = ex.message ?: "Error occured!")
            }
}

here is the resource class:

data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
    companion object {
        fun <T> success(data: T): Resource<T> = Resource(status = Status.SUCCESS, data = data, message = null)

        fun <T> error(data: T?, message: String): Resource<T> =
            Resource(status = Status.ERROR, data = data, message = message)

        fun <T> loading(data: T?): Resource<T> = Resource(status = Status.LOADING, data = data, message = null)
    }
}

here is the Repository:

class LoginRepository(private val apiHelper: ApiHelper) {
    suspend fun makeLogin(usr: User) = apiHelper.makeLogin(usr)
}

The return type of apiHelper.makeLogin(usr) is:


@JsonClass(generateAdapter = true)
data class LoginResponse(
    val token: String,
    val expiration: String,
    val id : Int,
    val role: Int)

The viewModel of the tutorial:

class MainViewModel(private val mainRepository: MainRepository) : ViewModel() {

    fun getUsers() = liveData(Dispatchers.IO) {
        emit(Resource.loading(data = null))
        try {
            emit(Resource.success(data = mainRepository.getUsers()))
        } catch (exception: Exception) {
            emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
        }
    }
}

In the tutorial he access the data stored in Resource in the main activity like this:

viewModel.getUsers().observe(this, Observer {
            it?.let { resource ->
                when (resource.status) {
                    SUCCESS -> {
                        recyclerView.visibility = View.VISIBLE
                        progressBar.visibility = View.GONE
                        resource.data?.let { users -> retrieveList(users) }
                    }
                    ERROR -> {
                        recyclerView.visibility = View.VISIBLE
                        progressBar.visibility = View.GONE
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                    LOADING -> {
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                }
            }
        })

Solution

  • You can do that simply by:

    class LoginViewModel(private val loginRepo: LoginRepository) : ViewModel() {
    private fun makeLogin(email: String, password: String) {
        viewModelScope.launch {
            Resource.loading(data = null)
            try {
    
                val usr = User(email, password)
                val response = loginRepo.makeLogin(usr)
                Resource.success(data = response)
                // HERE YOU HAVE ACCESS TO RESPONSE TO DO WHATEVER YOU WANT WITH IT
            } catch (ex: Exception) {
                Resource.error(data = null, message = ex.message ?: "Error occured!")
            }
    }