Using .addOnSuccessListener to return a value for a private method

Viewed 851

Good day. Is there some way that I could implement this one?

val db = Firebase.firestore
        val userID = Firebase.auth.currentUser!!.uid
        val infoRef = db.collection("user").document(userID).collection("profile").document("info")
        infoRef.get()
            .addOnSuccessListener {document ->
                if(document != null){
                    //get the data as cast to hashmap
                    val data = document.data as HashMap<*, *>
                    //get the username field and set text for greet user as the same value inside firestore
                    val username = data["username"] as String
                    tv_greet_user.text = "Hello, $username"
                }
            }

//extract the code above as a new method called "getUsername()"

val username : String = getUsername()

tv_greet_user.text = "Hello, $username"

2 Answers

Yes, you can create a function that looks like this:

fun getUsername(data: HashMap<String, Any>) = data["username"] as String

And inside your callback simply call:

val username = getUsername(data)

Is there a way to extract the whole block into a separate method? So that in the onCreate method, I could simply change the TextView into something like: val username = getUsername() tv_greet_user.text = "Hello, $username"

Edit:

As also Frank van Puffelen mentioned in his you cannot return the result of an asynchronous operation as a result of a method. Since you are using Kotlin programming, please note that there is actually o solution. I wrote an article called:

In which I explained four ways in which you can get data from Firestore. So if you are willing to use Kotlin Coroutines, then things will be much simpler.

Data is loaded from Firestore (and most modern web/cloud APIs) asynchronously. Since it may take some time before the data is available, your main code continues running while the data is being loaded. Then when the data is available, your success listener callback is called with that data.

This unfortunately means that it is impossible to return the value from the database in a function, because by the time the return statement runs, the data hasn't been loaded yet.

And that's also precisely why infoRef.get() in your code doesn't simply return the value from the database, but requires that you pass in a callback function that it invokes when the database is available. Sure, your code would be a lot simpler if get() would immediately return the value from the database, but it can't do that because the data needs to be loaded from the network.

I recommend reading:

Related