Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filling an array in Swift with Firebase dictionary

I am trying to fetch data from Firebase and add it as a object in a Swift array but whenever I run the application the array prints out empty. When I put a print statement inside the observe each purse prints out like I want it to but the purse never gets appended to the array.

var purses = [Purse]()

override func viewDidLoad() {
    fetchPurse(completion: {print(self.purses.count)})
}

func fetchPurse(completion: @escaping () -> ()){
    let ref = FIRDatabase.database().reference(fromURL: "https://test-database-ba3a2.firebaseio.com/")
    let user = (FIRAuth.auth()?.currentUser?.uid)!
    let userRef = ref.child("users").child(user).child("devices")

    userRef.observe(.childAdded, with: { (snapshot) in
        if let dictionary = snapshot.value as? [String: AnyObject]{
            let purse = Purse()
            purse.setValuesForKeys(dictionary)
            self.purses.append(purse)
            completion()
        }
    }, withCancel: nil)
}
like image 776
Ben Cavenagh Avatar asked May 17 '26 20:05

Ben Cavenagh


1 Answers

This sounds like a misunderstanding of how async methods work.

If you use code like this:

//at this point purses is empty..
fetchPurse()
print("purses count = \(purses.count)")

You will always see an empty purses array.

The problem is that the fetchPurse() function uses the Firebase function observe, which is asynchronous. It requests that Firebase run the code you pass as your with: closure when a new entry is added. The observe function returns immediately, and invokes the closure you pass to it as some future time when FireBase adds a new child object.

As a result, your fetchPurse() function also returns before the new purse object is added to your array of purses.

As @DávidPásztor said in his comment, you should refactor fetchPurse to take a completion handler that gets called when the purses array is updated:

func fetchPurse(completion: () -> ){
    let ref = FIRDatabase.database().reference(fromURL: "https://test-database-ba3a2.firebaseio.com/")
    let user = (FIRAuth.auth()?.currentUser?.uid)!
    let userRef = ref.child("users").child(user).child("devices")

    userRef.observe(.childAdded, with: { (snapshot) in
        if let dictionary = snapshot.value as? [String: AnyObject]{
            let purse = Purse()
            purse.setValuesForKeys(dictionary)
            self.purses.append(purse)
            //This is the new line that calls your new completion handler
            completion()
        }
    }, withCancel: nil)
}

And then you'd call it like this:

fetchPurse(completion: {
  print("purses count = \(purses.count)")
}
like image 81
Duncan C Avatar answered May 20 '26 12:05

Duncan C