0

I'm trying to retrieve data from an Asynchronous function of Firebase and then set it as the ViewController's user variable:

    var user : User!

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.dataSource = self
    fetchUser()
}


func fetchUser(){

    Api.User.observeCurrentUser { (user) in
        self.user = user

    }

The problem is that within the brackets of the 'observeCurrentUser' method I can actually see the user retrieved correctly but outside them the it is not associated to the self.user and when I try to print it outside the brackets it's nil.

Being said this, I searched on the web and I found out that this is because it's an asynchronous function. How can I get the self.user equal to the user retrieved in the 'observeCurrentUser' function so that I can work on it in the ViewController?

The Api.User.observeCurrentUser function retrieves the currentUser. It's coded in this way:

func observeCurrentUser(completion: @escaping (User)-> Void){

    guard let currentUser = Auth.auth().currentUser else {
        return
    }

    REF_USERS.child(currentUser.uid).observeSingleEvent(of: .value) { (snapshot) in
        if let dict = snapshot.value as? [String: Any]{
            let user = User.transformUser(dict: dict)
            completion(user)
        }
    }
}

Where REF_USERS it's the reference of all users in the Firebase-Database

14
  • 1
    What are you trying to do here? What is this Api.User.? You can access the currently authenticated user anywhere in your app with this code let user = Auth.auth().currentUser - can you clarify what you're asking? Also, please review the following two guides on asking questions: How do I ask a good question? and How to create a Minimal, Complete, and Verifiable example Commented Nov 16, 2019 at 14:57
  • Thanks for the correction @Jay: basically Api.User is a class created by be that contains a method (observeCurrentuser) that retrieves the user and pass it as an argument of the closure. Commented Nov 16, 2019 at 15:36
  • ...and...? Can you clarify what the question is and why that's needed? What's the actual issue? Commented Nov 16, 2019 at 15:43
  • The problem is that when I try to print the currentuser inside the closure it exists, but when I try to do this outside it it doesn't. How can I set the var self.user (that is a the view controller's variable) with the user retrieved in the closure? Commented Nov 16, 2019 at 15:48
  • Do what you have to do inside the closure in fetchUser or – more cumbersome – add a completion handler. Commented Nov 16, 2019 at 16:09

1 Answer 1

1

Outside the closure, the observe request hasn't completed. The correct approach is to do everything that you need to do with the retrieved user -- including updating the UI -- inside the closure.

func fetchUser(){
    Api.User.observeCurrentUser { (user) in
        self.user = user
        // alternatively, add this line to the user setter.  but for now...
        self.updateUIBecauseTheUserChanged()
    }
}

func updateUIBecauseTheUserChanged() {
    // change the UI, e.g. reloadData on the collection view
}
Sign up to request clarification or add additional context in comments.

2 Comments

I did what you said creating this function: func updateUIuserChanged(with user : User){ self.userNameLabel.text = user.username } and then passing the user data inside of the closure. but when I run the app it prints out this error : Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
I solved the problem, thanks everyone for the support. I implemented what you suggested me and it worked well. thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.