Code after guard is called later than expected

Hi I am new in iOS development and I am having hard time to understand the following issue. Basically I am trying to get user’s name by passing current user’s id to Cloud Firestore. However I am having hard time to understand a bug in the code. I can successfully pass the name of user to name variable, while the function returns default value of name which is "" (empty string). It seems that the block of code inside

if let data = snapshot?.data() {   guard let userName = data["name"] as? String else { return }   name = userName   print("after  guard") // this line } 

happens later than

print("name") // this line return name 

Full code:

private func returnCurrentUserName() -> String {         // User is signed in.         var name = ""         if let user = Auth.auth().currentUser  {                          let db = Firestore.firestore()             db.collection("users").document(user.uid).getDocument { (snapshot, error) in                 if error == nil {                     if let data = snapshot?.data() {                         guard let userName = data["name"] as? String else { return }                         name = userName                         print("after  guard") // this line                     }                 }             }                          print("name") // this line             return name         }else {             return ""         }     } 

(Note: the query from Cloud Firestore is successful and I can get users name on the console but "name" is printed after "after guard".)

Add Comment
2 Answer(s)

In addition to the other answer:
If you would like to execute code after your operation is done, you could use a completion block (that’s just a closure which gets called upon completion):

private func returnCurrentUserName(completion: @escaping () -> ()) -> String {         // User is signed in.         var name = ""         if let user = Auth.auth().currentUser  {                          let db = Firestore.firestore()             db.collection("users").document(user.uid).getDocument { (snapshot, error) in                 if error == nil {                     if let data = snapshot?.data() {                         guard let userName = data["name"] as? String else { return }                         name = userName                          completion()//Here you call the closure                         print("after  guard") // this line                     }                 }             }             print("name") // this line             return name         }else {             return ""         }     }  

How you would call returnCurrentUserName:

returnCurrentUserName {     print("runs after the operation is done") } 

Simplified example:

func returnCurrentUserName(completion: @escaping () -> ()) -> String {          DispatchQueue.main.asyncAfter(deadline: .now() + 4) {         completion() //runs after 4 seconds     }          return "xyz" }  let test = returnCurrentUserName {     print("runs after the operation is done") }  print(test) 
Add Comment

The reason is your getDocument is an asynchronous operation. It takes a callback, and that callback will be invoked when the operation is done. Because of the asynchronous operation, the program will continue process the next line without waiting for the async operation to be completed. That’s why you see your print("name") getting executed before the print("after guard")

Answered on August 31, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.