Reputation: 3302
I have my app setup using an MVVM architecture. The problem I am trying to solve is to trigger an api call based off two query parameters (in this case, an origin and destination), with this being dynamic and updating in realtime. When either of these value are updated, a check should be made that both are not null, and then an api call made based off the two values.
I have been playing with both MediatorLiveData
and Transformations.switchMap()
, but haven't been able to get a solution that works well. An attempt using MediatorLiveData:
class MyViewModel(val repository: AppRepository) : ViewModel() {
val origin = MutableLiveData<Location>()
val destination = MutableLiveData<Location>()
val directions = MediatorLiveData<DrivingDirections>()
init {
directions.addSource(origin) { origin ->
if (origin != null && destination.value != null) {
directions.value = // problem here
// I want to make a call to something like
// repository.getDirections(origin, destination), but this comes
// back as LiveData<Directions> so *can't* set it to directions.value
// I, in effect, want to make directions = repository.getDirections(origin, destination),
// but then I lose the Mediator functionality
}
}
directions.addSource(destination) {
// as above
}
}
}
So, trying to use switchMap, I created a crude OriginAndDestination
object, and then observed changes to that.
class myViewModel(val repository: AppRepository) : ViewModel() {
val originAndDestination = MutableLiveData<OriginAndDestination>()
val directions: LiveData<Directions>
init {
directions = Transformations.switchMap(originAndDestination) { originAndDestination ->
// probably should do some checks that both origin and destination are not null,
// so this switch map could return null? How do I guard against null in a switch map?
repository.getDirections(originAndDestination.origin, originAndDestination.destination)
}
}
fun setOrigin(location: Location) {
// bit of problem code here... need to retrieve the current value of
// originAndDestination.value, then update the 'origin' property,
// then set it to the liveData value again to trigger the switchMap, above... while checking that the value isn't null in the first place...
// something like:
val tempValue = originAndDestination.value
if (tempValue != null) {
// update tempValue.origin
} else {
// create a new OriginAndDestination object?
}
// just feels really messy
}
fun setDestination(location: Location) {
// As above
}
}
Sorry for all the comments, but it's highlighting some of the pain points and frustrations. Am I going about this all the wrong way? Origin
and Destination
are set from UI fields.
Upvotes: 1
Views: 459
Reputation: 1006944
This is more MVI than MVVM, but this is how I would approach it:
data class myViewState(
val origin: Location,
val destination: Location,
val directions: DrivingDirections
)
class myViewModel(val repository: AppRepository) : ViewModel() {
private val _states = MediatorLiveData<myViewState>()
val states: LiveData<myViewState> = _states
private val lastSource: LiveData<myViewState>? = null
fun updateLocations(origin: Location, destination: Location) {
lastSource?.let { _states.removeSource(lastSource) }
lastSource = repository.getDirections(origin, destination)
_states.addSource(lastSource) { directions ->
_states.value = myViewState(origin, destination, directions)
}
}
}
(where, with luck, we can get rid of lastSource
sometime in the future)
Your UI layer then observes states
and updates its UI for the locations and directions.
This may not fit your architecture, but it might give you some ideas.
Upvotes: 1