skip to Main Content

I have a Sign-Up Screen. On that screen, I get Username, Email, and Password. The Firestore API automatically checks if the Email is in use or not but I want to show the user if the Username entered is in use or not. To do it I used this code snippet :

fun userExists(element1 : String, element2 : String) : Boolean {

val db : FirebaseFirestore = FirebaseFirestore.getInstance()
val dbUsers : CollectionReference = db.collection("user")
var isExists : Boolean = false

dbUsers.whereEqualTo(element1, element2).get().addOnSuccessListener {
    // if true it is empty and it is not exists.
    isExists = it.documents.isNotEmpty()
}
return isExists

but here it skips and continues without returning the correct value. When I try this separately it works fine I understand if data exists or not but when I use it like this it doesn’t work. I have tried adding await() but it also has not worked. To run it under @Composable function I made the function suspend first after that I called this userExists function under rememberCoroutine but also couldn’t run it. The thing I want is simply checking for example userName: user1234 exists or not.
Thanks in advance.

Edit : Whole code.

 @OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SignUpScreen(navigation: NavController, auth: FirebaseAuth) {
    var userName by remember { mutableStateOf("") }
    var userEmail by remember { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }
    var passwordVisible by rememberSaveable { mutableStateOf(false) }
    val context = LocalContext.current
    val sharedPrefMail = context.getSharedPreferences("userEmail", Context.MODE_PRIVATE)
    val sharedPrefName = context.getSharedPreferences("userName", Context.MODE_PRIVATE)
    val sharedPrefScore = context.getSharedPreferences("highScore", Context.MODE_PRIVATE)
    val db : FirebaseFirestore = FirebaseFirestore.getInstance()
    val dbUsers : CollectionReference = db.collection("user")

    val buttonSize = with(LocalDensity.current) {
        (300f).toDp()
}
Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(vertical = buttonSize),
    horizontalAlignment = Alignment.CenterHorizontally
) {
    {...}
    // Toast message is not shown. Use PopUp message
    Button(onClick = {

        if (userEmail == "" || password == "" || userName == "") {
            Toast.makeText(context, "PLEASE ENTER EMAIL, USERNAME AND PASSWORD", Toast.LENGTH_LONG).show()
        } else if (userExists("userName", userName)) {
            Toast.makeText(context, "USERNAME IS IN USE", Toast.LENGTH_LONG).show()
        } else {
            auth.createUserWithEmailAndPassword(userEmail, password).addOnSuccessListener {
                // User creation is successful
                val user = UserObj(userEmail, userName, "0")
                dbUsers.add(user).addOnSuccessListener {
                    Toast.makeText(context, "USER CREATED SUCCESSFULLY", Toast.LENGTH_LONG).show()
                    sharedPrefMail.edit().putString("userMail", userEmail).apply()
                    sharedPrefName.edit().putString("userName", userName).apply()
                    sharedPrefScore.edit().putString("highScore", "0").apply()
                }
                navigation.navigate("FirstScreen")
            }.addOnFailureListener {
                Toast.makeText(context, "COULDN'T CREATE USER", Toast.LENGTH_LONG).show()
                println(it.localizedMessage)
            }


        }

    },
        modifier = Modifier.size(buttonSize, buttonSize/3)
    ) {
        Text("SIGN UP")
    }
}
}

3

Answers


  1. Chosen as BEST ANSWER

    Before @Faruk Karaca's answer I solved the problem. I am specifying this because it may work as well idk.

    Here is the whole code :

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun SignUpScreen(navigation: NavController, auth: FirebaseAuth) {
        val db : FirebaseFirestore = FirebaseFirestore.getInstance()
        val dbUsers : CollectionReference = db.collection("user")
    
        val mainScope = rememberCoroutineScope()
    
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(vertical = buttonSize),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            
            Button(onClick = {
                mainScope.launch(Dispatchers.Main) { // important
                    if (userEmail == "" || password == "" || userName == "") {
                        Toast.makeText(context, "PLEASE ENTER EMAIL, USERNAME AND PASSWORD", Toast.LENGTH_LONG).show()
                    } else if (userExists("userName", userName)) {
                        Toast.makeText(context, "USERNAME IS IN USE", Toast.LENGTH_LONG).show()
                    } else {
                        auth.createUserWithEmailAndPassword(userEmail, password).addOnSuccessListener {
                            val user = UserObj(userEmail, userName, "0")
                            dbUsers.add(user).addOnSuccessListener {
                                // Successful processes
                            }
                            navigation.navigate("FirstScreen")
                        }.addOnFailureListener {
                            //Failure processes
                        }
                    }
                }
            },
                modifier = Modifier.size(buttonSize, buttonSize/3)
            ) {
                Text("SIGN UP")
            }
        }
        }
    
    suspend fun userExists(element1 : String, element2 : String) : Boolean {
        val db : FirebaseFirestore = FirebaseFirestore.getInstance()
        val dbUsers : CollectionReference = db.collection("user")
    
        return !dbUsers.whereEqualTo(element1, element2).get().await().isEmpty
    
    }
    

    Under mainScope.launch I implemented my suspend function.


  2. You cannot return isExists as a result of a function. Firebase API is asynchronous. To solve this, you have to use a solution that is always the same. Any code that needs data from Firestore, needs to be inside the onSuccess() function or be called from there. However, a more modern approach will be to use Kotlin Coroutines.

    I have tried adding await() but it also did not work.

    This sentence doesn’t provide enough information so we can help. However, when you call await() it means that it will always await the completion of the task without blocking a thread. Please note that this function is a suspend function and not a blocking function. So your function should look like this:

    fun userExists(element1 : String, element2 : String) : Boolean {
        val db: FirebaseFirestore = FirebaseFirestore.getInstance()
        val dbUsers: CollectionReference = db.collection("user")
    
        return dbUsers.whereEqualTo(element1, element2).get().await().isEmpty
    }
    

    This method will check if the result of the query is empty or not.

    Login or Signup to reply.
  3. You can’t do it this way because it’s an asynchronous operation. Bu you can do it like this using flow and suspend functions.

    suspend fun isUserExists(element1: String, element2: String) = callbackFlow {
            try {
                val db: FirebaseFirestore = FirebaseFirestore.getInstance()
                val dbUsers: CollectionReference = db.collection("user")
    
                dbUsers.whereEqualTo(element1, element2).get()
                    .addOnSuccessListener {
                        trySendBlocking(it.documents.isNotEmpty())
                    }.addOnFailureListener {
                        // your error action
                    }
    
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    

    And collect like this,

    viewModel.isUserExists().collect { isUserNameExist ->
        //
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search