'Coroutine flow is not fetching latest data from Firestore after any change in collection
I want to fetch latest data from Firestore collection, but failed every time. As I am using MVVM so repository looks like:
class PurchaseRepository {
private val mPurchaseItemsCollection =
FirebaseFirestore.getInstance().collection(Constants.COLLECTION_PURCHASE)
/**
* Returns Flow of [State] which retrieves all PurchaseItems from cloud firestore collection.
*/
fun getAllItems() = flow<State<List<PurchaseItem>>> {
// Emit loading state
emit(State.loading())
val snapshot = mPurchaseItemsCollection.get().await()
val items = snapshot.toObjects(PurchaseItem::class.java)
// Emit success state with data
emit(State.success(items))
}.catch {
// If exception is thrown, emit failed state along with message.
emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
}
and ViewModel looks:
class CurrentMonthViewModel(private val repository: PurchaseRepository) : ViewModel() {
fun getAllItems() = repository.getAllItems()
}
and finally the Fragment is:
class CurrentMonthFragment : Fragment() {
private lateinit var currentMonthViewModel: CurrentMonthViewModel
private var _binding: FragmentCurrentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
// Coroutine Scope
private val uiScope = CoroutineScope(Dispatchers.Main)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentCurrentBinding.inflate(inflater, container, false)
val root: View = binding.root
currentMonthViewModel = ViewModelProvider(this, CurrentViewModelFactory())
.get(CurrentMonthViewModel::class.java)
val recyclerView = binding.recyclerviewCurrent
val adapter = CurrentMonthAdapter(root)
recyclerView.adapter = adapter
// Launch coroutine
uiScope.launch {
loadItems(root, adapter)
}
return root
}
private suspend fun loadItems(root: View, adapter: CurrentMonthAdapter) {
currentMonthViewModel.getAllItems().collect {
when(it){
is State.Loading -> {
showToast(root, "Loading")
}
is State.Success -> {
Log.e("__DATA__", it.data.toString())
adapter.submitList(it.data)
}
is State.Failed -> {
Log.e("__DATA__", it.message)
showToast(root, "Failed!")
}
}
}
}
private fun showToast(root: View, message: String) {
Toast.makeText(root.context, message, Toast.LENGTH_SHORT).show()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
with the code above App only fetches data on startup or restart, but not in real-time( means to say if new item is added to collection using firebase.console, it never shows new updates). I tried and other way by creating an other method in Repository as:
fun getAllItemsRealTime() = flow<State<List<PurchaseItem>>> {
val items = mutableListOf<PurchaseItem>()
mPurchaseItemsCollection.addSnapshotListener { value, error ->
if (error !== null) {
Log.e("addSnapshotListener","Error ${error.message}")
return@addSnapshotListener
}
value?.documents?.forEach { document ->
document.toObject(PurchaseItem::class.java)?.let {
it.purchaseId = document.id
items.add(it)
}
}
}
// Emit success state with data
emit(State.success(items))
}.catch {
// If exception is thrown, emit failed state along with message.
emit(State.failed(it.message.toString()))
}.flowOn(Dispatchers.IO)
and in ViewModel I tried as:
fun getAllItemsRealTime() = repository.getAllItemsRealTime()
and then in fragment changed loadItems() as:
private suspend fun loadItems(root: View, adapter: CurrentMonthAdapter) {
currentMonthViewModel.getAllItemsRealTime().collect {
when(it){
is State.Loading -> {
showToast(root, "Loading")
}
is State.Success -> {
Log.e("__DATA__", it.data.toString())
adapter.submitList(it.data)
}
is State.Failed -> {
Log.e("__DATA__", it.message)
showToast(root, "Failed!")
}
}
}
}
with this still same results as before. What is missing from above code? Or is there an other way to get the data and data changes in real time? Please suggest a solution.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
