alengqvist
alengqvist

Reputation: 501

Grand Central Dispatch in Swift only works the first run

I got a method which works like a refresher which uses the GCD pattern as shown below:

    func getStepsForTheWeek() {
    let concurrentQueue : dispatch_queue_t  = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

    dispatch_async(concurrentQueue, {

        // Create an array of Days.
        var days = [Day]()

        dispatch_sync(concurrentQueue, {
            print("first")

            for day in 0...7 {

                let date = self.getDate(day)

                // Get the date the day after that day.
                let endDate = self.getDateDayAfter(date)

                // Create a Day.
                var day = Day(date: date)

                self.pedometer.queryPedometerDataFromDate(date, toDate: endDate, withHandler: {numberOfSteps, error in
                    print("fetching")

                    if error != nil {
                        print("There was an error requesting data from the pedometer: \(error)")
                    } else {
                        day.steps = numberOfSteps!.numberOfSteps as Int
                        days.append(day)
                    }
                })
            }
        })

        dispatch_sync(dispatch_get_main_queue(), {
            print("second")

            self.historyViewController.days = days
            self.historyViewController.reloadHistory()
        })
    })
}

When the app starts the method works as it is intended to. But when the app is in the background and when I'm going back to the app I got this Observer which calls the method again to refresh its content.

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "appBecomeActive", name: UIApplicationWillEnterForegroundNotification, object: nil )

But everytime I do this the second code-block is running before the first one. Any help?

Upvotes: 0

Views: 128

Answers (2)

alengqvist
alengqvist

Reputation: 501

I solve it. In the query I check if the array of days is filled (e.g size of 8). When filled the reload should be done. I also deleted all unnecessary sync-tasks. Much cleaner now. It was simpler than I thought.

    func getStepsForTheWeek() {

    // Create an array of Days.
    var days = [Day]()

    print("first")

    // Fetch the total steps per day for 8 days (0 - 7).
    for day in 0...7 {

        // Get days date from today.
        let date = self.getDate(day)

        // Get the date the day after that day.
        let endDate = self.getDateDayAfter(date)

        // Create a Day.
        var day = Day(date: date)


        // Query the Pedometer for the total steps of that day.
        self.pedometer.queryPedometerDataFromDate(date, toDate: endDate) {
            (data, error) -> Void in
            if(error == nil){

                print("fetching")

                day.steps = data!.numberOfSteps as Int
                days.append(day)

                if(days.count == 8){
                    dispatch_async(dispatch_get_main_queue(), { () -> Void in

                        print("finished")

                        self.historyViewController.days = days
                        self.historyViewController.reloadHistory()
                    })
                }
            }
        }
    }
}

Thanks to Fonix and gnasher729.

Upvotes: 0

Fonix
Fonix

Reputation: 11597

i think you should do the UI update in the completion handler of the pedometer query

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), {

        self.pedometer.queryPedometerDataFromDate(date, toDate: endDate, withHandler: { numberOfSteps, error in
            if error != nil {
                print("There was an error requesting data from the pedometer: \(error)")
            } else {
                let numberOfStepsThisDay = numberOfSteps?.numberOfSteps as! Int

                day.steps = numberOfStepsThisDay
                days.append(day)
            }

            dispatch_async(dispatch_get_main_queue(), {
                self.historyViewController.days = self.days
                self.historyViewController.reloadHistory()
            })
        })
    })

you should always update the UI in the completion handler of the method if it has one, since you dont know whether it could be asynchronous or not (usually would be asynchronous if using a completion handler), you can probably drop the outer dispatch_async if there is no other code in it besides the pedometer query

Upvotes: 1

Related Questions