Scenario
List<Message>
in a composablemessages = mutableStateListOf<Message>
in viewModelmessages
depends on two actionsgetAll
orsearch
- In both cases we observe SQLite, but only one flow should be active at a single instant (user is either viewing all or is searching)
- Flow is used with SQLite because new messages can come from the network.
Questions
- How to switch between
getAll
andsearch
flow - When a new search happens the old
search
flow should be cancelled and a new one should start. How to do this?
Current implementation with two flows
@Composable
fun ListOfMessages() {
val messages = viewModel.messages
}
// -----------------------------
// ViewModel
// -----------------------------
@ExperimentalCoroutinesApi
class MessageViewModel(application: Application) {
val messages = mutableStateListOf<Message>()
val isLoading = mutableStateOf(false)
init {
fetchMessages()
}
fun fetchMessages() {
MessageUseCase(messagesDB).getAll().onEach { dataState ->
isLoading.value = dataState.loading
dataState.data?.let { data ->
messages.clear()
messages.addAll(data)
}
dataState.error?.let { error ->
// UIState -> error message
}
}.launchIn(viewModelScope)
}
fun SearchWithInMessage(q: String) {
MessageUseCase(messagesDB).search(q).onEach { dataState ->
isLoading.value = dataState.loading
dataState.data?.let { data ->
messages.clear()
messages.addAll(data)
}
dataState.error?.let { error ->
// UIState -> error message
}
}
}
}
// -----------------------------
// Use Cases
// -----------------------------
class MessageUseCase(messagesDB: messageDao) {
@ExperimentalCoroutinesApi
fun getAll(): Flow<DataState<List<Message>>> = channelFlow { {
send(DataState.loading())
try {
fetchAndSaveLatestMessagesFromRemote()
val messages = messagesDB.getAllStream()
messages.collectLatest { list ->
// business logic
send(DataState.success(list))
}
} catch (e: Exception){
send(DataState.error<List<Message>>(e.message?: "Unknown Error"))
}
}
@ExperimentalCoroutinesApi
fun search(q: String): Flow<DataState<List<Message>>> = channelFlow { {
send(DataState.loading())
try {
fetchAndSaveSearchedMessageFromRemote(q)
val messages = messagesDB.searchStream(q)
messages.collectLatest { list ->
// business logic
send(DataState.success(list))
}
} catch (e: Exception){
send(DataState.error<List<Message>>(e.message?: "Unknown Error"))
}
}
}
// -----------------------------
// DB and Network calls
// -----------------------------