Search code examples
androidkotlinviewmodelandroid-livedatakotlin-coroutines

I never enter inside the LiveDataScope inside my ViewModel


I have developed 2 functions for the login.

The first "loginOne" works when I use the ViewModel scope.

The other one doesn't work when I use the LiveData scope.

Do you have an idea? I want to make "loginTwo" work.

API

interface LoginAPI {

    @POST("login")
    suspend fun getUser(@Body loginRequest: LoginRequest): User
}

Repository

class LoginRepository(private val loginAPI: LoginAPI) {

    suspend fun getUser(loginRequest: LoginRequest) = loginAPI.getUser(loginRequest)
}

ViewModel

class LoginViewModel(private val loginRepository: LoginRepository) : ViewModel() {

    private var user: LiveData<User>? = null

    fun loginOne(username: String, password: String) {
        viewModelScope.launch {
            // i can enter here and get the user :)
            val user = loginRepository.getUser(LoginRequest(username, password))
            user
        }
    }

    fun loginTwo(username: String, password: String) {
        user = liveData(Dispatchers.IO) {
            // i never enter inside.. why ?
            val user = loginRepository.getUser(LoginRequest(username, password))
            emit(user)
        }
    }

    fun getUser(): LiveData<User>? = user
}

Fragment, my viewModel is injected with Koin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
   super.onViewCreated(view, savedInstanceState)

   loginViewModel.getUser()?.observe(this, Observer { user ->
       Log.d(LoginFragment::class.java.name, "User : $user ")
   })

   loginViewModel.loginOne("user","pcw123")
   loginViewModel.loginTwo("user","pcw123")
}

Solution

  • Make sure that you created Scope in the right way. Also, that you are using appropriate Dispatchers to achieve wanted results.

    You can additionally check if the call is being executed when you wanted to postValue. Check if Job is still alive.

    Check this thing. Your emmit call looks suspicious.

    When using LiveData, you might need to calculate values asynchronously. For example, you might want to retrieve a user's preferences and serve them to your UI. In these cases, you can use the liveData builder function to call a suspend function, serving the result as a LiveData object.

    Each emit() call suspends the execution of the block until the LiveData value is set on the main thread.

    In the example below, loadUser() is a suspend function declared elsewhere. Use the liveData builder function to call loadUser() asynchronously, and then use emit() to emit the result:

    val user: LiveData<User> = liveData {
        val data = database.loadUser() // loadUser is a suspend function.
        emit(data)
    }
    

    EDIT: MutableLiveData for user variable - resolved the issue.