Search code examples
androidkotlinviewmodelandroid-livedata

Kotlin - wait for multiple LiveData to be observe before running function


I am using a viewModel to pull livedata from a room data base. I have 2 LiveData that I pull from my viewModel and then I will run a function to pull data from my server. I need both values to be set before running my function that will get info from my server because these values are part of the body of the post.

This is part of my activity

var locationSeleted:Long=0L

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView<ActivityBinding>(this,R.layout.activity)

        //This is to get the viewmodel
        val application = requireNotNull(this).application
        val dataSource = Database.getInstance(application).DatabaseDao
        val viewModelFactory = ViewModelFactory(dataSource, application)
        val ViewModel = ViewModelProviders.of(this,viewModelFactory).get(ViewModel::class.java)

        binding.ViewModel = ViewModel
        binding.setLifecycleOwner (this)

        //Get location from livedata
        ViewModel.Location.observe(this, Observer {location ->

            if (location == null){
                //do something
            }
            else{
                locationSeleted = location.locationId
            }

        })

        //listens to the live data of the products value
        ViewModel.Products.observe(this, Observer{products ->
            
            if (products == null){
                //do something
            }
            else{
                myfunction(products)
            }

        })

This is part of my viewmodel

class ViewModel(val database: DatabaseDao, application: Application) : AndroidViewModel(application) {


    //This gets the location selected
    var Location: LiveData<LocationSelect> = database.get_location()

    //This line gets the products
    var Products: LiveData<Array<Products>> = database.get_products()


My current code works but I can imagen that if Location LiveData is delayed then myfunction will run with no location and the Post call will false. My question is how can I call myfunction when both location and products set from the viewModel? or is there a better way of doing this.

Thanks you


Solution

  • Edit: This can be done concisely using intermediate Flows, since Flows have convenient operators like combine.

    val locationAndProductsLiveData: LiveData<Pair<LocationSelect, Array<Products>>> = 
        location.asFlow().combine(products.asFlow()) { location, products ->
            location to products
        }.asLiveData()
    

    You can use a MediatorLiveData so you only have to observe one thing, and it fires only when both items are received, and on every change after that.

    In the ViewModel:

    val locationAndProductsLiveData: LiveData<Pair<LocationSelect, Array<Products>>> = 
        object: MediatorLiveData<Pair<LocationSelect, Array<Products>>>() {
            var location: LocationSelect? = null
            var products: Array<Products>? = null
            init {
                addSource(location) { location ->
                    this.location = location 
                    products?.let { value = location to it }
                }
                addSource(products) { products ->
                    this.products = products 
                    location?.let { value = it to products }
                }
            }
        }
    

    In your Activity:

    viewModel.locationAndProductsLiveData.observe(this) { (location, products) ->
        locationSelected = location.Id
        myFunction(products)
    }