0

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".)

Huseyn
  • 39
  • 7
  • 2
    because it inside a closure that runs in a different thread – aiwiguna Aug 20 '20 at 12:41
  • I will check about your answer online. Could you please guide me what changes would solve the problem? – Huseyn Aug 20 '20 at 12:43
  • 2
    Related: https://stackoverflow.com/questions/25203556/returning-data-from-async-call-in-swift-function – vadian Aug 20 '20 at 12:57
  • @SanzioAngeli There is no such things as _async thread_ or _run slower_, please don't confuse OP and other readers. – vpoltave Aug 20 '20 at 15:59
  • Okay I deleted the comment, try and revise if you can but trying to explain a concept in wording that's not highly technical – Sanzio Angeli Aug 21 '20 at 17:54

2 Answers2

2

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)
finebel
  • 2,227
  • 1
  • 9
  • 20
1

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")

congnd
  • 1,168
  • 8
  • 13