0

I am making an app with login, signup and so on. For each operation I have a different activity and, to clean up my code, I created a Kotlin class just for the server communication with methods for every server operation. Here it is:

package com.mainpackage.provachat

import com.google.firebase.auth.FirebaseAuth

class ServerCommunication {

    private var auth  = FirebaseAuth.getInstance()

    fun login(activity: android.app.Activity, email: String, psswd: String): Boolean{

        var isLogInSucceeded = false

        auth.signInWithEmailAndPassword(email, psswd)
            .addOnCompleteListener(activity) { task ->
                isLogInSucceeded = task.isSuccessful
            }

        return isLogInSucceeded
    }

    fun register(activity: android.app.Activity,  email: String, psswd: String): Boolean{

        var isSignUpSucceeded = false

        auth.createUserWithEmailAndPassword(email, psswd)
            .addOnCompleteListener(activity) { task ->
                isSignUpSucceeded = task.isSuccessful
            }

        return isSignUpSucceeded
    }

    //----THE METHODS BELOW WORK PERFECTLY------

    fun disconnectUser(){

        auth.signOut()
    }

    fun isCurrentUserLogged(): Boolean{

        if(auth.currentUser == null){
            return false
        }

        return true
    }
}

The problem comes when I try to use the method login() or register() infact they never succeed. I can tell it because I use a bool to verify if the task in addOnCompleteListener(activity) is completed correctly.

Here you are my call of the method login in an activity:

var isLoginSucceeded = sc.login(this, email, psswd)

I think that the problem is due to the activity object that I pass at register() or login() with this.

Thanks guys.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Have you tried to check what happens if the Task is not successful? – Alex Mamo Jun 08 '21 at 11:10
  • I found more in detail what is the problem: the lambda expression. The variable "isLogInSucceeded" is true while it is in the lambda but outside it's false. I found out it doing a print inside (true) and outside (false). So the firebase auth goes well the problem is the bool flag variable which has always false value when I return it. Why is that? Infact if I do not inizialize the variable, android studio says that I must inizialize it. Thanks. – Gian0207 Jun 08 '21 at 14:28
  • It's because Firebase API is asynchronous. You cannot simply use that value outside that method, as it takes some time to get the callback. – Alex Mamo Jun 08 '21 at 14:30
  • Ahh, got it. So what can I do to work around this problem? – Gian0207 Jun 08 '21 at 14:39
  • If you are interested in a Firebase Authentication with Google using Kotlin, you can check this [repo](https://github.com/alexmamo/FireApp/tree/master/app/src/main/java/ro/alexmamo/firebase/auth) out. – Alex Mamo Jun 08 '21 at 14:57

2 Answers2

0

This doesn't work the way you expect it to:

fun register(activity: android.app.Activity,  email: String, psswd: String): Boolean{

    var isSignUpSucceeded = false

    auth.createUserWithEmailAndPassword(email, psswd)
        .addOnCompleteListener(activity) { task ->
            isSignUpSucceeded = task.isSuccessful
        }

    return isSignUpSucceeded
}

If you run this code in a debugger (setting breakpoints on every line), or if you add logging you will see that return isSignUpSucceeded runs before isSignUpSucceeded = task.isSuccessful is ever executed. That is because the call to createUserWithEmailAndPassword is asynchronous, as it needs to communicate with the servers, and the main code (such as your return isSignUpSucceeded) continues to run, so that the user can continue to use you app.

In Java, the solution is to put any code that needs the result of the asynchronous call inside the completion listener, or to call it from there. That's pretty much what the task does in the code you already have, and also what is shown in this answer: Firebase - Android - fetchProvidersForEmail - Why are all the calls asynchronous?

With Kotlin, there are some other synchronization primitives though. I recommend you read more about them here: https://www.google.com/search?q=kotlin+wait+for+task+to+finish My best guess from reading some of those is that you can call await on the task, as shown here: FirebaseAuth - how can I wait for value

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

You can use callbacks, which you pass to the login function and it gets called on successful login when the task is completed.

fun login(activity: android.app.Activity, email: String, psswd: String, callback: (status: Boolean)->Unit){
    

    auth.signInWithEmailAndPassword(email, psswd)
            .addOnCompleteListener(activity) { task ->
                 if(task.isSuccessful){
                     callback.invoke(true)
                 }else{
                     callback.invoke(false)
                 }
            }.addOnFailureListener(activity) {
                callback.invoke(false)
            }
    
}

then use like this

 login(this,"hello","password"){ status ->
        //status is return here as a bool
    }