Christian Schmidt
Christian Schmidt

Reputation: 87

Firebase Observe called after following command

I am trying to load a username form a Firebase which is than supposed to be set in an Object. But the Firebase Observe Command is getting called after the name already gets set. What is the problem and how can I fix it?

let ref = Database.database().reference().child("Users").child(currentMessage.senderId).child("name")

ref.observeSingleEvent(of: .value, with: { (snapshot) in

    // This is supposed to be called first
    self.username = snapshot.value as! String
    print(self.username)
})

// This somehow gets called first
let nameModel = NameModel(name: self.username, uid: *some UID*)
decoratedItems.append(DecoratedChatItem(chatItem: nameModel, decorationAttributes: nil))

Upvotes: 0

Views: 648

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 598728

Firebase loads data from its database asynchronously. This means that the code executes in a different order from what you may expect. The easiest way to see this is with some log statements:

let ref = Database.database().reference().child("Users").child(currentMessage.senderId).child("name")
print("Before attaching observer")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
    print("In completion handler")
})
print("After attaching observer")

Unlike what you may expect, this code prints:

Before attaching observer

After attaching observer

In completion handler

This happens because loading data from Firebase (or any other web service) may take some time. If the code would wait, it would be keeping your user from interacting with your application. So instead, Firebase loads the data in the background, and lets your code continue. Then when the data is available, it calls your completion handler.

The easiest way to get used to this paradigm is to reframe your problems. Instead of saying "first load the data, then add it to the list", frame your problem as "start loading the data. when the data is loaded, add it to the list".

In code this means that you move any code that needs the data into the completion handler:

let ref = Database.database().reference().child("Users").child(currentMessage.senderId).child("name")    
ref.observeSingleEvent(of: .value, with: { (snapshot) in    
    self.username = snapshot.value as! String
    let nameModel = NameModel(name: self.username, uid: *some UID*)
    decoratedItems.append(DecoratedChatItem(chatItem: nameModel, decorationAttributes: nil))
})

For some more questions on this topic, see:

Upvotes: 2

Related Questions