Search code examples
iosswiftfirebasechartsios-charts

Can't load data for Charts


I'm using the Charts framework for my app. I can easy get values into the chart, if i'm giving it some static data. But when i try to fetch data from Firebase, it seems like the setChart function is being called before the fetching of the data is done.

Any suggestions? My GetLogs() function is being called from ViewDidLoad()

var dates = [String]()
var values = [Double]()

func getLogs() {
    DataService.ds.REF_PERIODS.child(period.periodId).child("logs").observeSingleEvent(of: .value, with: {(userSnap) in

        if let SnapDict = userSnap.value as? [String:AnyObject]{
            //Run through all the logs
            for each in SnapDict {
                DataService.ds.REF_LOGS.child(each.key).observeSingleEvent(of: .value , with : {(Snap) in

                    let postData = Snap.value as! Dictionary<String, AnyObject>
                    let log = Log(logId: each.key, postData: postData)

                    self.logs.append(log)

                    //Add the date to the the dates and values
                    self.dates.append(log.date)
                    self.values.append(Double(log.measurement)!)

                    //Sorts the logs by date
                    self.logs.sort(by: {$0.date > $1.date})
                    self.tableView.reloadData()

                })
            }
        }
        //Set the charts with the given information
        self.setChart(dataPoints: self.dates, values: self.values)
    })
}

But it can't show anything at the moment from Firebase.


Solution

  • Your code is structured incorrectly. You have an outer async call to FireBase. Inside that async call, you have a for loop that performs another async call for each item in the response dictionary. However, you attempt to set your chart before the inner async calls have completed, which won't work.

    EDIT:

    Since you are spawning a whole bunch of async calls in a for loop, you need logic that will keep track of how many of the inner calls have completed, and only update your chart once the last one has finished loading. Something like this:

    var dates = [String]()
    var values = [Double]()
    
    func getLogs() {
      DataService.ds.REF_PERIODS.child(period.periodId).child("logs").observeSingleEvent(of: .value, with: {(userSnap) in
        
        if let SnapDict = userSnap.value as? [String:AnyObject]{
          var completedCount = 0 //Keep track of how many items has been completed.
          //Run through all the logs
          for each in SnapDict {
            DataService.ds.REF_LOGS.child(each.key).observeSingleEvent(of: .value , with : {(Snap) in
              
              let postData = Snap.value as! Dictionary<String, AnyObject>
              let log = Log(logId: each.key, postData: postData)
              
              self.logs.append(log)
              
              //Add the date to the the dates and values
              self.dates.append(log.date)
              self.values.append(Double(log.measurement)!)
              
              //Sorts the logs by date
              self.logs.sort(by: {$0.date > $1.date})
              completedCount += 1
              if completedCount == snapDict.count {
                self.tableView.reloadData()
                //Set the charts with the given information
                self.setChart(dataPoints: self.dates, values: self.values)
              }
              
            })
          }
        }
      })
    }