I implemented an API call and I would like to allow the user to choose the sort order, keeping the choice saved in Datastore Preferences
.
But I couldn't implement this idea, since both are returning a Flow
, I don't know how to make one Flow wait for the other, and whenever the first one emits a new value, update the second.
Some example code I'm trying:
interface PreferencesRepository {
fun getFilter(): Flow<OrderType>
}
class PreferencesRepositoryImpl @Inject constructor() : PreferencesRepository {
override fun getFilter(): Flow<OrderType> = flow {
delay(timeMillis = 300) // simulate initial read delay from datastore
emit(value = OrderType.ASCENDING)
delay(timeMillis = 3000) // simulate a order change after 3 secs
emit(value = OrderType.DESCENDING)
}
}
interface ItemsRepository {
fun getItems(order: OrderType): Flow<List<ItemModel>>
}
class ItemsRepositoryImpl @Inject constructor() : ItemsRepository {
private val dummyItems: List<ItemModel> = listOf(
ItemModel(id = 1, title = "first item"),
ItemModel(id = 2, title = "second item"),
ItemModel(id = 3, title = "third item")
)
override fun getItems(order: OrderType): Flow<List<ItemModel>> = flow {
delay(timeMillis = 1000) // simulate network call delay
when (order) {
OrderType.ASCENDING -> {
emit(value = dummyItems)
}
OrderType.DESCENDING -> {
emit(value = dummyItems.sortedByDescending { it.id })
}
}
}
}
@HiltViewModel
class HomeViewModel @Inject constructor(
private val itemsRepository: ItemsRepository,
private val preferencesRepository: PreferencesRepository
) : ViewModel() {
private val _filter = MutableLiveData<OrderType>()
val filter: LiveData<OrderType> get() = _filter
private val _items = MutableLiveData<List<ItemModel>>()
val items: LiveData<List<ItemModel>> get() = _items
init {
// i imagine that these two methods must be unified somehow to work
getFilter()
getItems()
}
private fun getFilter() = viewModelScope.launch {
preferencesRepository.getFilter().collectLatest { orderType ->
_filter.value = orderType
println(orderType.name)
}
}
private fun getItems() = viewModelScope.launch {
itemsRepository.getItems(
order = _filter.value ?: OrderType.ASCENDING // not updated as expected
).collectLatest { items ->
_items.value = items
items.forEach { println(it.title) }
}
}
}
Logcat of current code:
CodePudding user response:
You can use either flatMapLatest() or alternatively flatMapConcat() operator:
preferencesRepository.getFilter()
.flatMapLatest { itemsRepository.getItems(it) }
Whenever there is a new order emitted, it invokes getItems()
with this order and then emits items from getItems()
.
flatMapLatest()
only cares about the last order emitted. If new order is emitted before items for the last one are acquired, it just ignores the previous order(s) and cancels fetching of items for it (or don't even start fetching). It seems like this is what you need.
flatMapConcat()
always invokes getItems()
for each new order and waits for items before processing the next ordering.
Also, if this is your real case and not a simplified example for StackOverflow, then I suggest to not re-fetch items when ordering changes. You can re-order locally.