Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android LiveData - switchMap is not triggered on second update

I have a LiveData object that depends on another LiveData. As I understand, Transformations.switchMap should allow to chain them. But switchMap handler is triggered only once and it doesn't react on further updates. If instead I use observe on the first object and, when it's ready, retrieve the second, it works fine but in this case I have to do it in Activity rather than ViewModel. Is it possible to chain LiveData objects, like Transformations.switchMap, but receive all updates, not only the first one?

Here is an attempt to use switchMap:

LiveData<Resource<User>> userLiveData = usersRepository.get();
return Transformations.switchMap(userLiveData, resource -> {
    if (resource.status == Status.SUCCESS && resource.data != null) {
        return apiService.cartItems("Bearer " + resource.data.token);
    } else {
        return AbsentLiveData.create();
    }
});

Here is an approach with observe in activity (works but requires to keep logic in activity):

viewModel.user().observe(this, x -> {
    if (x != null && x.data != null) {
        viewModel.items(x.data.token).observe(this, result -> {
            // use result
        });
    }
});
like image 729
Random Avatar asked Nov 21 '17 19:11

Random


2 Answers

I was trying to do something similar to you. I have a LiveData something, and when that changes I want to query somethingElse from the DB based on a property. Because the property can be null, if I query the DB with it, I'll get an exception. Therefore, if the property is null, I'm returning an empty MutableLiveData.

I have noticed, that when I returned this empty MutableLiveData, the observers that subscribed to somethingElse were not getting any updates. I saw that on your answer you ended up using a MediatorLiveData. Then I steped through my code with the debugger and noticed that the switchMap also uses a MediatorLiveData.

After experimenting a bit, I realised that when creating the empty MutableLiveData, it's initial value is null and won't trigger any updates. If I explicitly set the value, then it will notify the observers.

somethingElse = Transformations.switchMap(something, somethingObject -> {
                if (something.someProperty() != null) {
                    return repository.getSomethingElseByProperty(something.someProperty());
                }else{
                    MutableLiveData<SomethingElse> empty = new MutableLiveData<>();
                    empty.setValue(null);//need to set a value, to force update of observers
                    return empty;
                }

The code here has worked for me. In the question, you use an AbsentLiveData, which I don't know how it is implemented, so I'm not sure it would work exactly in that case.

like image 81
zelite Avatar answered Nov 09 '22 12:11

zelite


As a workaround, I used MediatorLiveData. I add the result of the first call as a source and, when it's ready, replace it with a final call:

MediatorLiveData<MyResponse> result = new MediatorLiveData<>();
LiveData<Resource<User>> source = this.get();
result.addSource(source, resource -> {
    if (resource.status == Status.SUCCESS && resource.data != null) {
        result.removeSource(source);
        result.addSource(apiService.cartItems("Bearer " + resource.data.token), result::postValue);
    }
});
return result;
like image 31
Random Avatar answered Nov 09 '22 13:11

Random