How and where to use Transformations.switchMap?

47,281

Solution 1

In the map() function

LiveData userLiveData = ...;
LiveData userName = Transformations.map(userLiveData, user -> {
     return user.firstName + " " + user.lastName; // Returns String
});

everytime the value of userLiveData changes, userName will be updated too. Notice that we are returning a String.

In the switchMap() function:

MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
    repository.getUserById(id)); // Returns LiveData

void setUserId(String userId) {
     this.userIdLiveData.setValue(userId);
}

everytime the value of userIdLiveData changes, repository.getUserById(id) will be called, just like the map function. But repository.getUserById(id) returns a LiveData. So everytime that the value of the LiveData returned by repository.getUserById(id) changes, the value of userLiveData will change too. So the value of userLiveData will depend on changes of userIdLiveData and changes of the value of repository.getUserById(id).

Practical example of switchMap(): imagine you have a user profile with a follow button and a next profile button which sets another profile info. Next profile button will call setUserId() with another id so userLiveData will change and UI will change. Follow button will call the DAO to add one follower more to that user, so the user will have 301 followers instead of 300. userLiveData will have this update that comes from the repository, which comes from the DAO.

Solution 2

Adding my 2 cents to @DamiaFuentes answer.

MutableLiveData userIdLiveData = ...;
LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
repository.getUserById(id)); // Returns LiveData

void setUserId(String userId) {
     this.userIdLiveData.setValue(userId);
}

Transformations.switchMap method will only be called when you have at least one observer for userLiveData

Solution 3

For those who want more explanation of @DamiaFuentes switchmap() function example given below:

 MutableLiveData userIdLiveData = ...;
 LiveData userLiveData = Transformations.switchMap(userIdLiveData, id ->
     repository.getUserById(id));

 void setUserId(String userId) {
      this.userIdLiveData.setValue(userId);
 }

In a scenario where the repository contains User(1, "Jane") and User(2, "John"), when the userIdLiveData value is set to "1", the switchMap will call getUser(1), that will return a LiveData containing the value User(1, "Jane"). So now, the userLiveData will emit User(1, "Jane"). When the user in the repository gets updated to User(1, "Sarah"), the userLiveData gets automatically notified and will emit User(1, "Sarah").

When the setUserId method is called with userId = "2", the value of the userIdLiveData changes and automatically triggers a request for getting the user with id "2" from the repository. So, the userLiveData emits User(2, "John"). The LiveData returned by repository.getUserById(1) is removed as a source.

From this example, we can understand that the userIdLiveData is the trigger and the LiveData returned by the repository.getUserById is the "backing" LiveData.

For more reference, check out: https://developer.android.com/reference/android/arch/lifecycle/Transformations

Solution 4

Another point to consider whether choosing between switchMap or map, you have to remember that map always wraps the returned value around LiveData e.g.

fun getUser(id: Int): User 
...
val userId = MutableLiveData(1)
val user = userId.map { // LiveData<User>
   repository.getUser(it)
}

You might consider using map if repository.getUser(it) returns a plain simple User object instead of LiveData so the type of user becomes LiveData<User>.

If repository.getUser(it) returns a LiveData<User> then it's better to use switchMap

fun getUser(id: Int): LiveData<User>
...
val userId = MutableLiveData(1)
val user = userId.switchMap { // LiveData<User>
   repository.getUser(it)
}

The user type would be LiveData<User>

Solution 5

The function passed to switchMap returns LiveData. Use it when your repository itself returns LiveData.

Share:
47,281
Ehtesham Hasan
Author by

Ehtesham Hasan

Updated on July 08, 2022

Comments

  • Ehtesham Hasan
    Ehtesham Hasan almost 2 years

    In recent Android Architecture Components library released by Google, we have two static functions in the Transformations class. While the map function is straight forward and easily understandable, I am finding it hard to properly understand the switchMap function.

    The official documentation of switchMap can be found here.

    Can somebody explain how and where to use the switchMap function with a practical example?

  • Android
    Android almost 6 years
    Thanks a ton @Prakash i was struggling to find out why my switchMap was not listening to the changes in trigger.
  • Maverick Meerkat
    Maverick Meerkat over 5 years
    so basically, it's a way to listen to a few change-sources of your data at once. If the id changes, your userLiveData changes, and if the values of the actual user changes, your userLiveData changes as well. Correct? (You could probably stack a few transformation together to connect even more LiveData's together, though you probably should use MediatorLiveData as well.)
  • TheRealChx101
    TheRealChx101 almost 5 years
    I used to be a simple man. When I couldn't understand this concept, I went and made something of my own. In this case, I created a whole class with custom DataSource, builders, etc until I hit a mental block. Your answer made be a simple man again. I deleted that class.
  • Gastón Saillén
    Gastón Saillén over 4 years
    Some architecture background of Transformations, they are most used at the ViewModel since it allows to transform the data of the type coming from the DAO to a type that needs to be shown at the UI, so, imagine you have an add to cart feature, you will be adding elements to a cart , lets say this cart is a hashmap that corresponds each product id with an item in a list, this hashmap comes from the DAO, but instead of passing this hashmap to the UI, we use a transformation to convert this hashmap into a friendly list to show in the UI, now HashMap<String,Cart> will be transformed as List<Cart>
  • Maarten
    Maarten about 4 years
    Is this a good example? It seems to imply that you make a database call in the switchMap callback, while the doc states: "The given function func will be executed on the main thread. "
  • Ruban
    Ruban over 3 years
  • Prajwal Waingankar
    Prajwal Waingankar over 3 years
    repository.getUserById(id); How do you handle the case when switchmap() is called on the getUserById(), the mutabledata != null condition
  • Prajwal Waingankar
    Prajwal Waingankar over 3 years
    How to set default value for transformations query?
  • Prajwal Waingankar
    Prajwal Waingankar over 3 years
    How to set default query for the search string in switchmap?
  • RoK
    RoK over 3 years
    Maarten, we should only return the userLiveData for observing and along with that do actual async call to the database and then call postValue on this livedata.