1

I have this code in which I am trying to observe a variable from my viewmodel. However, whenever I observe the variable, it always returns false, which is the default value, even though it should be returning true. I don't understand why it's not working, any idea and advice would be great.

This is the viewmodel part:

val isSuccessful = MutableLiveData(false)

fun acceptAgreement() = currentAgreement.value?.let {
       viewModelScope.launch {
           runCatching { agreementsRepository.acceptAgreement(it.id) }
               .onSuccess { isSuccessful.postValue(true) }
               .onFailure { isSuccessful.postValue(false) }
       }
   }

The observation in the fragment, where it always returns the showError():

binding.btnAccept.setOnClickListener { onAccept().also { continue()} }
private fun onAccept() = viewModel.acceptAgreement()

private fun continue() {
      viewModel.isSuccessful.observe(viewLifecycleOwner, {
           if (it) { start() } else { showError() }
       })
   }

Repository:

suspend fun acceptAgreement(id: String) = changeAgreement(id, status.ACCEPTED)

private suspend fun changeAgreement(id: String, status: status) {
       try { agreementsService.changeAgreement(id, status.serialize()) }
       catch (e: Throwable) { logger.error(this::class.java.name, "Failed to change status ${id}", e) }
   }
dazai
  • 766
  • 4
  • 25

3 Answers3

1

Is there a reason you are running continue() after your run onAccept?

I believe what is happening is you haven't set the observer before you are observing.

So your flow goes:

onAccept -> triggers the update of the livedata. Continue -> Sets the observer of the livedata.

I would suggest that you move the method call "continue()" into your onCreateView method of the fragment. It won't be triggered until it changes state in the viewmodel anyway.

Also you need to check you have set the viewLifecycleOwner of the fragment.

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    val binding =  FragmentYourFragmentNameBinding.inflate(inflater, container, false).apply {
        lifecycleOwner = viewLifecycleOwner
    }

    continue()

    return binding.root
}
adrem7
  • 388
  • 1
  • 3
  • 14
  • Thank you for the response. I find that since I have an error modal on continue(), if I move to oncreate, since the variable is false by default, the modal appears right away :/ – dazai Feb 23 '22 at 11:41
  • Please remove false from the definition of isSuccessful. You can initialise as val isSuccessful = MutableLiveData() You then don't trigger the dialog. Note: If you hover to the left of the = and hit alt/opt enter you can choose "specify type explicitly" - this will show you that isSuccessful remains MutableLiveData and not MutableLiveData. Given you are initialising, you can initialise with empty value without changing the type to a nullable. – adrem7 Feb 24 '22 at 13:59
  • Alternatively you can (and should) use lazy initialisation. This means the livedata is only triggered when it is first interacted with. You would do this as follows: val isSuccessful: MutableLiveData by lazy { MutableLiveData(false) } – adrem7 Feb 24 '22 at 14:01
0
 isSuccessful.postValue(
            runCatching { agreementsRepository() }.isSuccess
        )
GTID
  • 538
  • 2
  • 6
  • 19
  • I can't use agreementsRepository() but I can use agreementsRepository, which doesn't seem to work – dazai Feb 22 '22 at 16:47
0

Instead of using isSuccessful.postValue() use isSuccessful.value = true. I have found that assignment, not the postValue method, updates registered observers for LiveData.

  • Set value should only be called from the main thread - if you want to do this you need to have a decent awareness of thread safety. In the viewModel can you be sure you are calling this from the main thread, if so, great, if not, it can cause issues. Posting the value is generally safer. – adrem7 Feb 22 '22 at 22:29
  • Not necessarily the case -- I've used coroutines (viewmodelscope) and set the value of `MutableLiveData` in many instances without ANR or any other problem. Since view models don't update the UI directly, this should pose no problem – ryanflynn001 Feb 22 '22 at 22:56
  • Its worth mentioning that I ran into a similar issue when I first started using observers and I was also using `postValue`. When I switched to setters, my problems resolved. – ryanflynn001 Feb 22 '22 at 22:58
  • thank you but unfortunately, the issue is still there – dazai Feb 23 '22 at 11:43