1

I set up a label and a progress indicator to bind to the AppDelegate's progress property. I then perform work on a concurrent queue. As each task finishes, I increase the progress by 1.

My problem is that the label updates tick-by-tick as expected but the progress indicator doesn't. It updates once every 15 ticks or so. Any idea how to make the progress indicator to move with every tick?

A simplified example:

class AppDelegate: NSObject, NSApplicationDelegate {
    dynamic var progress = 0

    @IBAction func updateProgress(sender : AnyObject) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            guard self.progress < 100 else {
                return
            }

            self.progress += 1
            sleep(1)
            self.updateProgress(sender)
        }
    }
}
Jenny
  • 2,041
  • 13
  • 15

2 Answers2

1

In my experience, updating binding variable from a background queue sometimes lead to funny behaviors. The progress indicator not updating is an example. I've not figured out the "why" part though. My workaround is to do your work on the background queue, but update the binding variable on the main queue.

Try this (not tested):

@IBAction func updateProgress(sender : AnyObject) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        guard self.progress < 100 else {
            return
        }

        dispatch_async(dispatch_get_main_queue()) {
            self.progress += 1
        }

        sleep(1)
        self.updateProgress(sender)
    }
}

I noticed that your concurrent queue is anything but concurrent in the example. I assume you use background threads to perform multiple tasks at once. If so, incrementing progress on the main queue also help with race condition because the main queue is a serial one, so all progress increments are performed one by one.

Code Different
  • 90,614
  • 16
  • 144
  • 163
0

Changes to UI should be update on the main thread. You should move the update progress to main thread.

dispatch_async(dispatch_get_main_queue()) {
    self.updateProgress(sender)
}
Thanh Pham
  • 57
  • 3
  • Thanks for your reply. The reason I used Cocoa binding is to take advantage of KVO to update multiple controls at once. Why does it work for one control but not the other? – Jenny Jan 08 '16 at 02:20
  • i think you can read this http://stackoverflow.com/questions/12693197/dispatch-get-global-queue-vs-dispatch-get-main-queue – Thanh Pham Jan 08 '16 at 02:43
  • This is a bad advice. If `updateProgress` takes a long time to finish, it will lock your main queue and freeze your UI. – Code Different Jan 08 '16 at 02:52