0

(Android, Kotlin)
I'm trying to recover data from firebase through a repository and It is happening correctly but in the wrong time

override suspend fun getAllOnline(): MutableStateFlow<ResourceState<List<DocModel>>> {
    val docList: MutableList<DocModel> = mutableListOf()
    auth = FirebaseAuth.getInstance()
    database
        .child(auth.currentUser!!.uid)
        .addValueEventListener(object: ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                for(docs in snapshot.children) {
                    val doc = docs.getValue(DocModel::class.java)
                    docList.add(doc!!)
                }
            }

            override fun onCancelled(error: DatabaseError) {
                return
            }
        })
    return if(docList.isNullOrEmpty()) {
        MutableStateFlow(ResourceState.Empty())
    } else {
        MutableStateFlow(ResourceState.Success(docList))
    }
}

The problem is: my doc list is populated after the return finishes. I've debugged and logged it and the result always come after the function is ended, so it return no data. It is necessary to somehow only allow the return when the data retrieve is completed.
Any suggestions?
Thanks in advance

Tods
  • 37
  • 6
  • 1
    Don’t use listeners in a suspend function. Adding a listener is like saying, “do this in the future”. Instead, use `await()` in a try/catch. I’m AFK but there are many examples of this on the web and in previous questions on this site. – Tenfour04 Dec 30 '22 at 03:03
  • Since you're using Kotlin, I think that this [resource](https://medium.com/firebase-tips-tricks/how-to-read-data-from-firebase-realtime-database-using-get-269ef3e179c5) will help. Here is the corresponding [repo](https://github.com/alexmamo/RealtimeDatabase). – Alex Mamo Dec 30 '22 at 08:46
  • I'm trying to use the database to continuously displaying the updated value to my recycler. Since I need realtime updates, those options still would be available? I was reading and I got a few answers saying it is not possible: [example](https://stackoverflow.com/questions/66875595/how-to-use-firestore-databse-addsnapshotlistener-using-await-in-kotlin). I need somehow to receive realtime updates to be able to delete specific files after if the user needs. – Tods Dec 30 '22 at 12:54
  • @AlexMamo I'm trying to use the database to continuously displaying the updated value to my recycler. Since I need realtime updates, those options still would be available? I was reading and I got a few answers saying it is not possible: [here](https://stackoverflow.com/questions/66875595/how-to-use-firestore-databse-addsnapshotlistener-using-await-in-kotlin). I need somehow to receive realtime updates to be able to delete specific files after if the user needs. – Tods Dec 30 '22 at 14:46
  • For real-time updates, I think that this [resource](https://medium.com/firebase-tips-tricks/how-to-make-a-clean-architecture-android-app-using-mvvm-firestore-and-jetpack-compose-abdb5e02a2d8) will help. Here is the corresponding [repo](https://github.com/alexmamo/FirestoreCleanArchitectureApp). And I strongly recommend you use Kotlin Coroutines rather than attaching listeners. – Alex Mamo Dec 31 '22 at 08:08

1 Answers1

0

You can either use await or if you want the code remain this way, you can also use suspendCoroutine like below:

private suspend fun getFirebaseToken(): String? {
    return try {
        val suspendCoroutine = suspendCoroutine<Task<String>> { continuation ->
            FirebaseMessaging.getInstance().token.addOnCompleteListener {
                continuation.resume(it)
            }
        }
        if (suspendCoroutine.isSuccessful && suspendCoroutine.result != null)
            suspendCoroutine.result
        else null
    } catch (e: Exception) {
        e logAll TAG
        null
    }
}

suspendCoroutine<Task<String>> can be replaced by suspendCoroutine<MutableList<DocModel>>

And you will pass docList in "continuation.resume(docList)" instead of "it":

Your final code will look like this:

override suspend fun getAllOnline(): MutableStateFlow<ResourceState<List<DocModel>>> {

auth = FirebaseAuth.getInstance()

val docList = suspendCoroutine<MutableList<DocModel>>{ continuation->
  database
    .child(auth.currentUser!!.uid)
    .addValueEventListener(object: ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            val docList: MutableList<DocModel> = mutableListOf()
            for(docs in snapshot.children) {
                val doc = docs.getValue(DocModel::class.java)
                docList.add(doc!!)
            }
            continuation.resume(docList)
        }

        override fun onCancelled(error: DatabaseError) {
            continuation.resume(emptyList<DocModel>())
        }
    })
}
return if(docList.isSuccessful && docList.result != null && 
       docList.result.isNullOrEmpty()) {
    MutableStateFlow(ResourceState.Success(docList.result))
} else {
    MutableStateFlow(ResourceState.Empty())
}

}

Zeeshan Ali
  • 623
  • 1
  • 5
  • 10
  • I have implemented it and now it's working properly.. but I'm having another problem related to it. Now, when displayed my list and I want to delete one item, it crashes the app because the list was not updated again.. any suggestions? – Tods Dec 30 '22 at 17:11
  • The event correctly delete the specified file but it is not able to continue without crashing – Tods Dec 30 '22 at 17:11
  • You need to manage state of the list either locally by removing the deleted item from list, after successful deletion and updating ui manually, or you can attach live event listener which will update data in realtime. Further if it doesn't solve your issue you need to provide the error and code you are using! – Zeeshan Ali Dec 31 '22 at 10:23
  • I’ll try asap and if it does not work, may I open another question and you help? – Tods Dec 31 '22 at 12:53
  • I don't think there is any need to post a question for this! Hopefully we'll resolve it here! – Zeeshan Ali Dec 31 '22 at 16:56
  • I tried to manage the list locally but the same exception is thrown "already resumed" at "continuation.resume(docList)". How it would be to add an event listener in this situation? My code for recovering the data is 95% like the one you suggested. – Tods Jan 01 '23 at 03:50
  • Well I think it would be best to post another question! – Zeeshan Ali Jan 01 '23 at 10:55
  • I just created, [here](https://stackoverflow.com/questions/74977848/how-to-make-my-result-from-firebase-database-continuously-update-at-realtime) – Tods Jan 01 '23 at 22:50