'LiveData Transformation not getting triggered

I subscribed to ids and search in the ui but i wasn't getting any results so i stepped through with the debugger and found out that the transformation is not getting triggered after the first time. So when i call setIds the first time ids gets updated but for every call after the first one the transformation won't trigger. Same goes for the search.

Any ideas what might possible go wrong?

class MyViewModel : ViewModel() {

    private val repository = Repository.sharedInstance

    var recentRadius: LiveData<List<RecentRadius>>?
    var recentRoute: LiveData<List<RecentRoute>>?

    init {
        recentRadius = repository.recentRadius()
        recentRoute = repository.recentRoute()
    }


    private val idsInput = MutableLiveData<String>()
    fun setIdsInput(textId: String) {
        idsInput.value = textId
    }

    val ids: LiveData<List<String>> = Transformations.switchMap(idsInput) { id ->
        repository.ids(id)
    }

    private val searchInput = MutableLiveData<Search>()
    fun setSearchInput(search: Search) {
        searchInput.value = search
    }


    val search: LiveData<SearchResult> = Transformations.switchMap(searchInput) { search ->
        when (search.type) {
            SearchType.ID -> repository.id(search)
            SearchType.RADIUS -> repository.radius(search)
            SearchType.ROUTE -> repository.route(search)
        }
    }
}


Solution 1:[1]

Below example illustrates use of map when observer is attached in the activity.

Activity

class MainActivity : AppCompatActivity() {

lateinit var mBinding : ActivityMainBinding

private val mViewModel : MainViewModel by lazy {
   getViewModel { MainViewModel(this.application) }
}

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
   mBinding.vm = mViewModel

   // adding obeserver
   mViewModel.videoName.observe(this, Observer<String> { value ->
       value?.let {
           //Toast.makeText(this, it, Toast.LENGTH_LONG).show()
       }
   })
 }
}

ViewModel with map

class MainViewModel(val appContext : Application) : AndroidViewModel(appContext) {

   private val TAG = "MainViewModel"

   var videoData = MutableLiveData<VideoDownload>()
   var videoName : LiveData<String>

   init {
      // Update the data
      videoName = Transformations.map(videoData) { "updated : "+it.webUrl }
    }

    fun onActionClick(v : View) {
       // change data
       videoData.value = VideoDownload(System.currentTimeMillis().toString())
    }

    fun onReActionClick(v : View) {
       // check data
       Toast.makeText(appContext, videoName.value, Toast.LENGTH_LONG).show()
    }

}

ViewModel with switchMap

class MainViewModel(val appContext : Application) : AndroidViewModel(appContext) {

    private val TAG = "MainViewModel"

    var videoData = MutableLiveData<VideoDownload>()
    var videoName : LiveData<String>

    init {
       // Update the data
       videoName = Transformations.switchMap(videoData) { modData(it.webUrl) }

    }

    private fun modData(str: String): LiveData<String> {
        val liveData = MutableLiveData<String>()
        liveData.value = "switchmap : "+str
        return liveData
    }

    fun onActionClick(v : View) {
        // change data
        videoData.value = VideoDownload(System.currentTimeMillis().toString())
    }

    fun onReActionClick(v : View) {
        // check data
        Toast.makeText(appContext, videoName.value, Toast.LENGTH_LONG).show()
    }

}

Solution 2:[2]

for me, it was because the observer owner was a fragment. It stopped triggering when navigating to different fragments. I changed the observer owner to the activity and it triggered as expected.

itemsViewModel.items.observe(requireActivity(), Observer {

The view model was defined as a class property:

    private val itemsViewModel: ItemsViewModel by lazy {
    ViewModelProvider(requireActivity()).get(ItemsViewModel::class.java)
}

Solution 3:[3]

If you really want it to be triggered.

fun <X, Y> LiveData<X>.forceMap(
    mapFunction: (X) -> Y
): LiveData<Y> {
    val result = MutableLiveData<Y>()
    this.observeForever {x->
        if (x != null) {
            result.value = mapFunction.invoke(x)
        }
    }
    return result
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 fullmoon
Solution 3 rahat