0

I am trying to add a document to Firestore but only if it doesn't exist before. I tried finding some existing answers on SO but they talk about having security rules for Firebase. But I want to do it programmatically.

What I have done so far :

I have a simple layout which have one edit text called as etDeviceCode. I am asking user to enter a code(let's say any string) and if it already exists I just want a toast to show that it exists otherwise perform the task to add it. User authentication is already done.

Here is code:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //Initialising firestore and auth
        firestore = FirebaseFirestore.getInstance()
        auth = FirebaseAuth.getInstance()

        binding.btnAddDevice.setOnClickListener {
            val device = binding.etAddDevice.text.toString()
            saveInFirestore(device)
        }
    }
 private fun saveInFirestore(device: String) {
        //Firestore document reference
        val docsRef = firestore.collection("Devices Registered").document(device)

        val addDevice: MutableMap<String, String> = HashMap()
        addDevice["UID"] = auth.uid!!
        addDevice["Device Name"] = device


        if (!verifyFirebase()) {
            Timber.d("It already exists!!!!")
        } else {

            docsRef.set(addDevice)
                .addOnSuccessListener {
                    Timber.d("New device saved with name : $device")
                    Toast.makeText(requireContext(), "New Device Saved", Toast.LENGTH_SHORT).show()
                }
                .addOnFailureListener {
                    Toast.makeText(requireContext(), "Not able to save Device", Toast.LENGTH_SHORT)
                        .show()
                    Timber.d("Unable to add device")
                }
        }
    }
private fun verifyFirebase(): Boolean {

        var valid = true
        val device = binding.etAddDevice.text.toString()
        val docRef = firestore.collection("Devices Registered").document(device)

        docRef.get()
            .addOnSuccessListener { document ->
                if (document != null) {
                    Timber.d("Document data is : ${document.data}")
                    val response = document.getString("Device Name")
                    Timber.d(response)
                    if (response == device) {
                        Timber.d("Device already exists by name : $response")
                        valid = false
                    }
                } else {
                    Timber.d("No such doc exists")
                }
            }
            .addOnFailureListener {
                Timber.d("Failed with exception : $it")
            }
        Timber.d("valid : $valid")
        return valid
    }

I have applied a logic in verifyFirebase to check if it already exists using Boolean.

Now the main issue I am facing: The value of valid which is converted to false when the document is already there at line

Timber.d("Device already exists by name : $response")
valid = false
}

but at the end when I am returning the value it is still true. I checked the logs and found that it returns before even entering addOnSuccessListener so obviously it is returning true. But why? I don't get it, why is it returning true and why is it getting executed before the addOnSuccessListener.

If you have any understanding about this let me know in comments. It would be highly appreciated.

Edit 1: Complete Code

oyeraghib
  • 878
  • 3
  • 8
  • 26
  • 1
    That is the expected behavior. Data is loaded from Firestore (and most cloud APIs) asynchronously, and while that is happening in the background your main code (including the `return valid`) continues to execute. Then when the data is available your success listener is called, which sets `valid`. What this means is that any code that needs the data from the database has to be inside the success listener, be called from there, or be synchronized some other way. See the question I linked for more on this. – Frank van Puffelen Jul 26 '22 at 15:28
  • @FrankvanPuffelen thanks for your quick response. I went through that answer and understood the logic. But I am still struggling with getting it done in Kotlin. There is no `onSuccess()` in Kotlin so don't know how to execute that block of code. – oyeraghib Jul 26 '22 at 17:16
  • 1
    I'm not a Kotlin expert myself, but this seems to have some promising lads: https://stackoverflow.com/search?q=%5Bgoogle-cloud-firestore%5D%5Bkotlin%5D+asynchronous – Frank van Puffelen Jul 27 '22 at 00:11
  • 1
    I think that this [resource](https://medium.com/firebase-tips-tricks/how-to-read-data-from-cloud-firestore-using-get-bf03b6ee4953) will also help. – Alex Mamo Jul 27 '22 at 08:08
  • Thanks a lot @FrankvanPuffelen and @Alex Mamo, I fixed this issue finally. I think that working with two functions in this case was my mistake. As things are happening asynchronously on Firebase taking value from one function to another is causing the problem. I fixed it by doing everything inside one single function and using `onSuccessListener` and `onCompleteListener` wherever required. – oyeraghib Jul 27 '22 at 14:30

0 Answers0