Home > Enterprise >  How to return values only after observing LiveData in Android [Kotlin]
How to return values only after observing LiveData in Android [Kotlin]

Time:09-09

I have been facing this issue for quite sometime and would like to know a better approach to solve this problem. If you are aware of anything about how to solve it then please let me know.

I am building a project which takes data from an API and then following MVVM architecture I take the Retrofit instance to Repository, and then to ViewModel and further observe it from my fragment.

Now what I am working on currently is Login feature. I will send a number to the API and in response I will receive if the number is registered or not. If it is registered then I would move to the next screen.

Now the problem is that using one of the function in ViewModel I send that number to the API to get the response. And using a variable I observe that response.

Now I create a function which checks if the response was true or false and based on that I am using the logic to move to the next screen, but the issue is the returned value from the function. As LiveData works asynchronously in background it takes some time to return the value and in meantime the function returns the initial value which is false.

Function to verify response

    private fun checkNumber(): Boolean {
        var valid = false
        authRiderViewModel.response.observe(viewLifecycleOwner, Observer {
            Timber.d("Response: $it")
            if (it.success == true) {
                valid = true
            }
        })
        Timber.d("Boolean: $valid")
        return valid
    }

Moving to next screen code:

binding.btnContinue.setOnClickListener {
            val number = binding.etMobileNumber.text.toString().toLong()
            Timber.d("Number: $number")
            authRiderViewModel.authDriver(number)

            if (checkNumber()) {
                val action = LoginFragmentDirections.actionLoginFragmentToOtpFragment()
                findNavController().navigate(action)
            } else {
                Toast.makeText(requireContext(), "Number not registered", Toast.LENGTH_SHORT).show()
            }
        }

So in case I received the true response from the server even then I would not move to the next screen because the initial value I received is false. I have spent few hours trying to fix it and any help would be appreciated. If you need any code let me know in comments. Thanks.

CodePudding user response:

You have four distinct states:

  • The server returned a positive response
  • The server returned a negative response
  • The server failed (e.g., returned a 500, timed out)
  • You are waiting on the server

You are attempting to model that with two states: true and false. This will not work.

Instead, model it with four states. One approach is called "loading-content-error" and uses a sealed class to represent those states:

sealed class LoginState {
  object Loading : LoginState()
  data class Content(val isSuccess: Boolean) : LoginState()
  object Error : LoginState()
}

Your LiveData (or your StateFlow, once you migrate to coroutines) would be a LiveData<LoginState>. Your observer can then use a when to handle Loading, Content, and Error as needed, such as:

  • For Loading, display a progress indicator
  • For Content, do whatever you are doing now with your boolean
  • For Error, display an error message

CodePudding user response:

Actually, live data observation is an asynchronous operation. You have to code accordingly.

Just calling checkNumber() won't return since is asynchronous instead I give you some ideas to implement in a better way.

Just call the checkNumber when button click inside the check number do this instead of return valid

authRiderViewModel.response.observe(viewLifecycleOwner, Observer {
        Timber.d("Response: $it")
        if (it.success == true) {
            val action = LoginFragmentDirections.actionLoginFragmentToOtpFragment()
            findNavController().navigate(action)
        } else {
            Toast.makeText(requireContext(), "Number not registered", Toast.LENGTH_SHORT).show()
        }
    })
  • Related