0

I have an issue which should not be an issue in my opinion.

When I load this 'simple' function, it should return the users within the DB.

 - returns usersDictionary: All the users in a dictionary
*/
func getAllUsers() -> Dictionary<String, AnyObject> {
     var usersDict = Dictionary<String, AnyObject>()

    if userIsLoggedIn() == true{
        FIRDatabase.database().reference().child("users").observeSingleEvent(of: .value, with: { (snapshot) in
            if let userDictionary = snapshot.value as? [String:AnyObject]{
                for user in userDictionary{

                    usersDict.updateValue(user.value as AnyObject
                        , forKey: user.key as String)


                }
                print("TEST")
                print(usersDict)
            }
            print("TEST2")
            print(usersDict)
        })
        print("TEST3")
        print(usersDict)
        return usersDict

    }
    return usersDict
}

However it does not return usersDict with value, but just empty which is strange because it prints out the value of TEST and TEST2 usersDict.

But Test3 remains empty, how is this possible? If I look in the Console, it does Test3 first and then test and then test2.

This is what console prints:

TEST3
[:]
TEST
"ZuUiTSu142P5NTc9VfekRbuFcny2": {
    address = "testAddress";
    email = "test@outlook.com";
    name = "testuser";
    phonenumber = "1234567890";
    rol = Customer;
}]
TEST2
"ZuUiTSu142P5NTc9VfekRbuFcny2": {
    address = " testAddress ";
    email = " test@outlook.com ";
    name = "testuser";
    phonenumber = "1234567890";
    rol = Customer;
}]
MisterJFK
  • 1
  • 2
  • It's not strange at all: `observeSingleEvent` works **asynchronously**, the data is returned later in the closure. You need a completion handler, returning something directly is impossible. – vadian Apr 09 '17 at 19:00
  • Possible duplicate of [Returning data from async call in Swift function](http://stackoverflow.com/questions/25203556/returning-data-from-async-call-in-swift-function) – Keiwan Apr 09 '17 at 19:53

2 Answers2

0

@vadian is right on spot with his comment: You are trying to synchronously return a value in a function while you are fetching the data asynchronously. Everything happening with your // lines of code here:

FIRDatabase.database().reference().child("users").observeSingleEvent(of: .value, with: { (snapshot) in
  // ...
  // lines of code
  // ...
})

Is happening asynchronously. Meaning: it happens in the background, and whatever code you have after the observeSingleEvent call is executed first, hence you see TEST3 as the first line of output.

There are several different ways to design your code around an Asynchronous Model. Probably the easiest of which would be passing a Callback function to be called once the Async code finishes executing.

Sounds confusing? Don't worry, it IS confusing at first, but once you get it you'll be amazed by how simple the concept is.

I am not going to explain how Callbacks and Async works, but I would point you towards a similar problem that has a very good answer. Give it a look and you should get the idea.

Best of luck!

Community
  • 1
  • 1
Marwan Alani
  • 286
  • 1
  • 6
0

Thank you guys for the answers, maybe a little bit too late because I skipped this part for a while and focused on other things, but I looked at it today and managed to do it which on the end was very easy.

 func loadBusiness(busUID: String,with completion: @escaping(_ business: [String:AnyObject], _ error: NSError?) -> Void){
        FIRDatabase.database().reference().child("businesses").child(busUID).observeSingleEvent(of: .value, with: { (snapshot) in
            if let busDict = snapshot.value as? [String:AnyObject]{
                completion(busDict, nil)
            }
        }) { (error) in
            print("Error occured: " + error.localizedDescription)
        }
    }

Just a simple example on how I manage to do it and I can call it everywhere now with:

// Load basic company info
        db.loadBusiness(busUID: db.getMyUID()) { (business, error) in
            self.myCompany = business
        }
MisterJFK
  • 1
  • 2