0

I made a function that connects to firebase, goes to a specific path and then gives me the value. When I print(snapshot.value), it gives me the value I need. When I call the function, userProfile is empty. I need userProfile to return the snapshot String.

 func getUserData(uid: String) -> String{
   var _REF_USERNAME = FIRDatabase.database().reference().child("users").child(uid).child("profile").child("username")

     var userProfile = String()


    _REF_USERNAME.observe(.value, with: {(snapshot) in
        print("SNAP: \(snapshot.value)")
            userProfile = snapshot.value as! String

    })
    print(userProfile)
    return userProfile
}
AL.
  • 36,815
  • 10
  • 142
  • 281
Chris
  • 437
  • 1
  • 4
  • 18
  • 1
    The return is executing before data is returned from Firebase. Firebase data becomes valid *only* inside the closure. Please see my answer to [this question](http://stackoverflow.com/questions/43823808/access-firebase-variable-outside-closure/43832208#43832208) and also [this one](http://stackoverflow.com/questions/43130730/table-view-is-not-displaying-the-comments-or-username-from-firebase/43157576#43157576). You should not generally attempt return data from a Firebase function as it's asynchronous where are the rest of your code is synchronous. – Jay May 08 '17 at 19:22

1 Answers1

2

Swift 3

You are calling userProfile outside of the callback in observe, so it'll be executed before the observe functions completes asynchronously. Callbacks are difficult to understand at first, but the general idea is that your code does not run sequentially from top to down, and there are some parts of the code that are run asynchronously in a background thread.

To get access to userProfile, you have to pass in a callback function just like that:

func observeUserProfile(completed: @escaping (_ userProfile: String?) -> ()) -> (FIRDatabaseReference, FIRDatabaseHandle) {

    let ref = _REF_USERNAME

    let handle = ref.observe(.value, with: {(snapshot) in
            if !snapshot.exists() { // First check if userProfile exists to be safe
                completed(nil)
                return
            }
            userProfile = snapshot.value as! String
            // Return userProfile here in the callback
            completed(userProfile)
    })
    // Good practice to return ref and handle to remove the handle later and save memory
    return ref, handle

And you call this observe code outside while passing in the callback function like that:

let ref, handle = observeUserProfile(completed: { (userProfile) in
     // Get access to userProfile here
     userProfile
}

And when you're done with this observer, stop it from observing to save memory by doing the following:

ref.removeObserver(withHandle: handle)
leonardloo
  • 1,753
  • 3
  • 20
  • 31