0

Okay I am reading from a database and when I print the individual variables they print out correctly. However it seems like the data refuses to append to the array. Anyone know why? I can't figure it out at all.

let commuteBuilder = Commutes()

    Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in


        //print(snapshot)

        if let dict = snapshot.value as? NSDictionary {
            commuteBuilder.distance = dict["Distance"] as! Double
            commuteBuilder.title = dict["TripName"] as! String
            commuteBuilder.transportType = (dict["Transport"] as? String)!

        }

        commuteArray.append(commuteBuilder)
    })
    print("helper")
    print(commuteArray.count)
    return commuteArray
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
Robert Ryder
  • 39
  • 1
  • 8

1 Answers1

1

The data is correctly added to the array, just not at the time that you print the array's contents.

If you change the code like this, you can see this:

let commuteBuilder = Commutes()

Database.database().reference().child("Users").child(user).child("Trips").observe(DataEventType.childAdded, with: { (snapshot) in

    if let dict = snapshot.value as? NSDictionary {
        commuteBuilder.distance = dict["Distance"] as! Double
        commuteBuilder.title = dict["TripName"] as! String
        commuteBuilder.transportType = (dict["Transport"] as? String)!

    }

    commuteArray.append(commuteBuilder)
    print("added one, now have \(commuteArray.count)")
})
print("returning \(commuteArray.count)")
return commuteArray

You'll see it print something like this:

returning 0

added one, now have 1

added one, now have 2

etc.

This is likely not the output you expected. But it is working as intended. Firebase loads data from its database asynchronously. Instead of blocking your code, it lets the thread continue (so the user can continue using the app) and instead calls back to the code block you passed to observe when new data is available.

This means that by the time this code returns the array it is still empty, but it later adds items as they come in. This means that you cannot return data from a function in the way you are trying.

I find it easiest to change my way of thinking about code. Instead of "First get the data, then print it", I frame it as "Start getting the data. When data comes back, print it".

In the code above, I did this by moving the code that prints the count into the callback block. Instead of doing this, you can also create your own callback, which is called a completion handler or closure in Swift. You can find examples in this article, this article, this question Callback function syntax in Swift or of course in Apple's documentation.

Community
  • 1
  • 1
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Okay this makes more sense to me and I get why this might be useful. My issue is I previously set up my code to use CoreData in which case I was calling the old data and dumping it into an array with this function. Well now I want to use firebase for this. I get how I could use this to populate say a table view that automatically updates but I also have use cases where I want to add values (the distance values) and output a total distance. The only way id know to do that is to dump it into an array and perform a summing function. Seems like I cant do that easily with they Async type function. – Robert Ryder Jun 23 '17 at 02:43
  • You just have to do it differently. When you listen for `.child*` events, you can keep a running aggregate: just a sum of the values so far. Alternatively, you can llisten for `.value` events, which give you all child nodes in one go. Just iterate over them, and calculate the sum each time something changes. See https://firebase.google.com/docs/database/ios/lists-of-data#listen_for_value_events – Frank van Puffelen Jun 23 '17 at 03:51