Search code examples
swiftmacosswift2cocoa-bindings

Progress Indicator does not animate


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)
        }
    }
}

Solution

  • 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.