Deyan Deyan - 4 months ago 29
Swift Question

How to properly add data from Firebase into Array?

I'm trying to get results from Firebase and put them into Array, but it seems I miss something. What I want is to get 'Time' and 'Blood Glucose" values from Firebase and to put them into an arrays which I will use for Charts. I'm able to put the data into 'BG' and 'TIME' arrays, but when I 'append' them into 'FetchedDate' and 'FetchedBG' I see empty arrays (FetchedBG and FetchedDate)

var FetchedDate:[String]! = []
var FetchedBG: [Double]! = []

//GET DATA FROM FB
func GetDetails(){
let posts = rootRef.child("Diary/\(userID!)/\(passedDATE!)")
//let posts = rootRef.queryOrderedByChild(passedDATE!)
posts.observeEventType(FIRDataEventType.Value , withBlock: { (snapshot) in

for list in snapshot.children {
if let BG = list.value.objectForKey("Blood Glucose")!.doubleValue {
self.FetchedBG.append(BG)
print(BG) // SHOWS RESULTS AS EXPECTED
}
if let TIME = list.value.objectForKey("Time") {
self.FetchedDate.append(TIME as! String)
print(TIME) // SHOWS RESULTS AS EXPECTED
}
}
}) { (error) in
print(error.localizedDescription)
}
}

override func viewDidLoad() {
super.viewDidLoad()

GetDetails()

print(FetchedDate) // EMPTY ARRAY
print(FetchedBG) // EMPTY ARRAY

Answer

Firebase loads (and synchronizes) the data from your database asynchronously. Since that may take some time, your code continues executing and you print the arrays while they're still empty.

Once a value is available (either for the first time or when the data has changed), your block is invoked. It adds the data to the arrays. But by that time your print statements have long finished.

The solution is to move the code that needs to run when the value is available (or when it has changed) into the block. E.g.

var FetchedDate:[String]! = []
var FetchedBG: [Double]! = []

//GET DATA FROM FB 
func StartSynchronizingDetails(){
    let posts = rootRef.child("Diary/\(userID!)/\(passedDATE!)")
    //let posts = rootRef.queryOrderedByChild(passedDATE!)
    posts.observeEventType(FIRDataEventType.Value , withBlock: { (snapshot) in

        for list in snapshot.children {
            if  let BG = list.value.objectForKey("Blood Glucose")!.doubleValue {
            self.FetchedBG.append(BG)
                print(BG) // SHOWS RESULTS AS EXPECTED
            }
            if let TIME = list.value.objectForKey("Time") {
            self.FetchedDate.append(TIME as! String)
                  print(TIME) // SHOWS RESULTS AS EXPECTED
            }
        }
        print(FetchedDate)
        print(FetchedBG)
    })  { (error) in
        print(error.localizedDescription)
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    StartSynchronizingDetails()

This is a very common pattern when your app interacts with (potentially time-consuming) network resources. It is also precisely the reason Firebase's observeEventType takes a withBlock: argument: to isolate the code that starts synchronizing from the code that responds to value updates.