Navigate to the next screen only after Firestore tasks are complete SwiftUI

86 Views Asked by At

I'm performing certain Firestore tasks in my App like Signing u/Signing out. I want to Navigate to the Home Screen of my App after the Sign-up/Login is successfully completed and a FirebaseAuth.User has been set in my Model.

I've written an Asynchronous function to Sign-up/Login the user and Task to perform the Async task. However, even though I have await signUp() in my code, the code does not wait for the task to get over and I end up getting nil data. The User gets created using the Email/Password provider in the Authentication tab but when I try to add a User document in my Users collection for the newly created user that's where it gets nil values, for e.g. the authDataResult?.user.uid is nil since the process is still going on.

Below is my Controller Code:

func signUp(email:String, password:String) async throws{
        
        Auth.auth().createUser(fName:String, lName:String, withEmail: email, password:password){ [self] authResult, error in
            
            
            guard let result = authResult else{
                print(#function, "Error while signing up a new user \(error)")
                return
            }
            
            print(#function, "AuthResult: \(result)")
            
                self.user = authResult?.user
                
                UserDefaults.standard.set(self.user?.email, forKey: "KEY_EMAIL")
                UserDefaults.standard.set(password, forKey: "KEY_PASSWORD")
            
    let newUser = UserData(uid: authResult?.user.uid, fName:fName, lName:lName, email:email, password:password)


 await FireDBHelper.shared.insertUser(newUserData:UserData)

print("User added to Users collection with id: \(authResult?.user.uid)")
            
        }//createUser
        
        
    }//signUp

FireDBHelper class Code:

func insertUser(newUserData:UserData) async throws{
        
//This is where the Application breaks since the newUserData.uid is not set since the //previous operation hasn't finished yet. 
        try store.collection(COLLECTION_USERS).document(newUserData.uid).setData(from: newUserData){ err in
            
            if let err = err{
                
                print("Error during insert operation \(err.localizedDescription)")
                
                return
            }else{
                print("Users collection created for DOC ID: \(newUserData.uid)")
            }
        }
    }

I'm calling the signup function from my View as follows:

Button("Sign up"){
  Task(priority:.background){

do{

     try await fireAuthHelper.signUp()

     //This gets executed right after the above call and doesn't wait for the above 
     call/task to finish
     print("All Operations completed successfully")

   //Navigate to the Home Screen

     } catch{
       print("Error performing sign up \(error.localizedDescription)")
     }

   }
}

Where am I going wrong? Trying to wrap my head around this for hours now. Please could somebody give me some suggestions?

UPDATE------ Thanks lorem ipsum for the suggestion, I changed my above method to use the below code that uses Firebase's async functions, though, I had to run my code on the MainActor for it to run.

Below is the code:

@MainActor
    func signupUser(firstName:String, lastName:String, emailAdd:String, passwd:String) async throws -> Bool{
        
        self.showProgressView = true
        
        let authDataResult = try await Auth.auth().createUser(withEmail: emailAdd, password: passwd)
        
        let uid = authDataResult.user.uid
        
        let tempUser = UserData(uid: uid, firstName: firstName, lastName: lastName, email: emailAdd, password: passwd)
        
        UserDefaults.standard.set(authDataResult.user.email, forKey: "KEY_EMAIL")
        
        UserDefaults.standard.set(uid, forKey: "KEY_USER_ID")
        
        UserDefaults.standard.set(passwd, forKey: "USER_PASSWORD")
        
        let reference = Firestore.firestore().collection("Users").document(uid)
        
        self.signedInUser = tempUser
        
        do{
            
           let userData =  try await reference.getDocument(as: UserData.self)
            
//            self.signedInUser = userData
            
        }catch{
            
            let reference = Firestore.firestore().collection("Users").document(uid)
            
            try reference.setData(from: tempUser){ err in
                
            }
            
            self.isLoggedIn = true
            
            self.showProgressView = false
            
        }
        if(self.signedInUser == nil){
            return false
        }
        return true
    }//signUpUser
    

Also, will look into keychains, I'm new to iOS Development so this is a kind of a learning curve for me.

Although the above code works, how can I do what I want to achieve using the Firebase's createUser() method that uses a closure? Is using an escaping completion handler the only way to do this?

Auth.auth().createUser(fName:String, lName:String, withEmail: email, password:password){ [self] authResult, error in{
}
0

There are 0 best solutions below