How to chain transformations in Android when using live data?

Naveed picture Naveed · Nov 12, 2017 · Viewed 9.8k times · Source

Given the following setup:

I have 2 repositories: Repository A and Repository B both of them return live data.

I have a ViewModel that uses both of these repositories.

I want to extract something from Repository A and depending on the result I want to grab something from Repository B and then transform the result before returning to UI.

For this I have been looking at the LiveData Transformation classes. The examples show a single transformation of the result however I want something along the lines of chaining two transformations. How can I accomplish this?

I have tried setting something up like this but get a type mismatch on the second transformation block:

  internal val launchStatus: LiveData<String> = Transformations
        .map(respositoryA.getData(), { data ->
            if (data.isValid){
                "stringA"
            } else {
                //This gives a type mismatch for the entire block
                Transformations.map(repositoryB.getData(), {
                    result -> result.toString()
                })
            }
        })

(Also please let me know if there is an alternate/recommended approach for grabbing something for chaining these call i.e. grab something from A and then grab something from B depending on result of A and so on)

Answer

Chrispher picture Chrispher · Oct 11, 2018

Your lambda sometimes returns the String "stringA", and sometimes returns the LiveData<String> given by:

Transformations.map(repositoryB.getData(), {
    result -> result.toString()
})

This means that your lambda doesn't make sense - it returns different things in different branches.

As others have mentioned, you could write your own MediatorLiveData instead of using the one given by Transformations. However, I think it's easier to do the following:

internal val launchStatus: LiveData<String> = Transformations
    .switchMap(respositoryA.getData(), { data ->
        if (data.isValid) {
            MutableLiveData().apply { setValue("stringA") }
        } else {
            Transformations.map(repositoryB.getData(), {
                result -> result.toString()
            })
        }
    })

All I've done is made the first code branch also return a LiveData<String>, so now your lambda makes sense - it's a (String) -> LiveData<String>. I had to make one other change: use switchMap instead of map. This is because map takes a lambda (X) -> Y, but switchMap takes a lambda (X) -> LiveData<Y>.