Sam
Sam

Reputation: 243

How to make app wait until Firebase request is finished

I want my method to wait until Firebase request finished

uploadSingUpInfo returns before the Firebase request finishes, and this is a problem for me - some method returns nil.

static func uploadSingUpInfo(fullName:String,email:String,password:String)->String{
    rootRef = FIRDatabase.database().reference()
    var returnVlue="not valid"

    FIRAuth.auth()?.createUserWithEmail(email, password: password) { (user, error) in
        if (error != nil){
            returnVlue=(error?.userInfo["error_name"]) as! String
        }
        else{

            let newUser = [
                "username": fullName
            ]
            rootRef.childByAppendingPath("User")
                .childByAppendingPath((user?.uid)!).setValue(newUser)
            NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isLogin")
            NSUserDefaults.standardUserDefaults().setObject(email, forKey: "email")
            NSUserDefaults.standardUserDefaults().setObject(user?.uid, forKey: "user_ID")
            print(NSUserDefaults.standardUserDefaults().objectForKey("user_ID"))
            returnVlue="valid"

        }
    }

    return returnVlue

}

Upvotes: 4

Views: 8943

Answers (3)

Tommy
Tommy

Reputation: 989

Instead of returning a value from that function. You can call another function inside the firebase method at the "very end" of the method. This will ensure that whatever function you need to do next, is executed at the end when Firebase is done processing your request

FIRAuth.auth()?.createUserWithEmail(email, password: password) { (user, error) in
        if (error != nil){
            returnVlue=(error?.userInfo["error_name"]) as! String
        }
        else{

            let newUser = [
                "username": fullName
            ]
            rootRef.childByAppendingPath("User")
                .childByAppendingPath((user?.uid)!).setValue(newUser)
            NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isLogin")
            NSUserDefaults.standardUserDefaults().setObject(email, forKey: "email")
            NSUserDefaults.standardUserDefaults().setObject(user?.uid, forKey: "user_ID")
            print(NSUserDefaults.standardUserDefaults().objectForKey("user_ID"))
            returnVlue="valid"

        }
callAnotherFunctionWhenFinished()
    }

Upvotes: 1

Jay
Jay

Reputation: 35648

Don't use Firebase as functions that return values - it goes against it's asynchronous nature.

Plan code structure that allows Firebase to perform it's task and then within the closure (block) go to the next step.

For example: In your code, change the function to not return anything and within the createUserBlock, as the last line instead of return, call the next function to update your UI.

static func uploadSingUpInfo(fullName:String,email:String,password:String) {

    rootRef = FIRDatabase.database().reference()

    FIRAuth.auth()?.createUserWithEmail(email, password: password) { (user, error) in
        if (error != nil){
            showUserAnError(error)
        } else {

            let newUser = [
                "username": fullName
            ]
            rootRef.childByAppendingPath("User")
                .childByAppendingPath((user?.uid)!).setValue(newUser)
            NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isLogin")
            NSUserDefaults.standardUserDefaults().setObject(email, forKey: "email")
            NSUserDefaults.standardUserDefaults().setObject(user?.uid, forKey: "user_ID")
            print(NSUserDefaults.standardUserDefaults().objectForKey("user_ID"))
            continueLoginProcess()  //reload the ui or whatever step is next
        }
      }
    }

Upvotes: 5

ShahiM
ShahiM

Reputation: 3268

In your code, the method returns before the firebase request completes so the value of returnVlue will not be set.

Normal Functions don't wait for async functions to finish running ;- because that's kind of the point of an async function. isn't it?

So if you want your uploadSingUpInfo to wait until createUserWithEmail has finished, then you have to make createUserWithEmail an async function as well by using completionHandlers:

func uploadSingUpInfo(fullName: String, email: String, password: String, completion: (result: String) -> Void) {
    FIRAuth.auth()?.createUserWithEmail(email, password: password) {
     (user, error) in
       if (error != nil){
           returnVlue=(error?.userInfo["error_name"]) as! String
       }
       else{

           let newUser = [
               "username": fullName
           ]
           rootRef.childByAppendingPath("User")
            .childByAppendingPath((user?.uid)!).setValue(newUser)
           NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isLogin")
           NSUserDefaults.standardUserDefaults().setObject(email, forKey: "email")
           NSUserDefaults.standardUserDefaults().setObject(user?.uid, forKey: "user_ID")
           print(NSUserDefaults.standardUserDefaults().objectForKey("user_ID"))
           returnVlue="valid"

       }

       completion(returnVlue);
    }
}

And this is how you would call this method :

uploadSingUpInfo(<arguments>) {
    (result: String) in             //this is what you pass to completion handler.
     print("got back: \(result)")
}

See this article for a better idea.

Upvotes: 4

Related Questions