2

Hi I am having difficulty in getting the values of an array inside a Firebase block

My code involves working out the numbers of multiple food items ordered (requestFoodItemArray[i]) and append them to an array (requestFoodNumberArray). I am able to get it successfully to run but then the values stored inside requestFoodNumberArray will disappear when I try to access it anywhere else except within the observe() block.

How can I "extend" the lifespan of the data stored in that particular array?

Thanks!

 override func viewDidLoad() {
    super.viewDidLoad()

    populateCharts(completion: { requestNumberArray in
        print(requestNumberArray)
    })

}

func populateCharts (completion: @escaping (_ requestNumberArray: Array<Double>) -> Void)  {
    for i in 0 ..< requestFoodItemArray.count {
    refRequest.child("\(requestFoodItemArray[i])").observe(.value, with: { snapshot in
        var newItem: [FoodRequestItemList] = []
        for child in snapshot.children {
            if let snapshot = child as? DataSnapshot,
                let requestItemList = FoodRequestItemList(snapshot: snapshot) {
                newItem.append(requestItemList)
            }
        }
        self.item = newItem
        self.requestedItemArray = self.item.map({ $0.key })
        var arrayCount : Double = Double(self.requestedItemArray.count)
        var requestArray : [Double] = []
        requestArray.append(arrayCount)
        completion(requestArray)
    }, withCancel: nil)
}

1 Answers1

0

Data is loaded from Firebase asynchronously. While the data is being loaded, the rest of your code continues to execute. Then when the data is available, your closure (snapshot in) is called.

The code that is finding requestFoodNumberArray empty, is simply run before the closure has been called, and thus the requestFoodNumberArray is still empty.

You can most easily see this for yourself by adding some simple log statements:

print("Before attaching observer")
refRequest.child("\(requestFoodItemArray[i])").observe(.value, with: { snapshot in
  print("Got data")
})
print("After attaching observer")

If you run this code, you'll get the following output:

Before attaching observer

After attaching observer

Got data

That is probably not the order in which you expected the data. But it does explain precisely why the array is empty in the code after-but-outside the closure: the data hasn't been loaded yet.

To not have this problem, all code that requires the data must be (called from) within the closure. Alternatively (if you're comfortable with low-level synchronization) you can use synchronization primitives to make the call after the closure wait.

See:

Community
  • 1
  • 1
Frank van Puffelen
  • 418,229
  • 62
  • 649
  • 645
  • Thanks for the advice! I have tried to implement a completion handler - see updated code but for some reason, the requestArray after append(arrayCount) still only returns one double value instead of a whole array. Not sure what am I doing wrong? Help, please! Thank you~ –  Dec 26 '18 at 13:18
  • All you're adding to `requestArray ` is a count: `requestArray.append(arrayCount)`, so that's all that is available in the completion handler. Are you sure you're not looking to pass `newItem` to the completion handler? – Frank van Puffelen Dec 26 '18 at 20:11
  • I am trying to loop through all the food items listed in the "requestFoodItemArray" using for loop and get their respective number ordered using "var arrayCount : Double = Double(self.requestedItemArray.count)" Then append this to requestArray so that I can use the array data to populate a chart. Not sure how can I have a complete set of numbers ready for the chart? If the chart is outside the closure, it gets no data due to async nature. If it the chart is called within the closure, the looping nature means it has only one number available. –  Dec 26 '18 at 23:21
  • Yeah, there's a bit much going on there. I recommend running your code in a debugger and setting some breakpoints. For example, it seems you now call `completion(requestArray)` for each child node that you load, meaning you call it each time with a single number. You probably want to call it only once, with all numbers, but that's not what your code does. – Frank van Puffelen Dec 27 '18 at 05:31