1

I'm trying to make a nice timer using Swift and GCD. I find lots of blogs and even an entry in Matt Neuburg's book. I used the latter to put together my own variant in a playground:

import Foundation
struct Timer {
    private var queue = dispatch_queue_create("timer", nil)
    private var source: dispatch_source_t
    var tick:()->() = {} {
        didSet {
            self.update()
        }
    }
    var rate:Double = 1.0 {
        didSet {
            self.update()
        }
    }

    init() {
        self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue)
        self.update()
    }

    func cancel() {
        dispatch_source_cancel(self.source)
    }

    func update() {
        dispatch_source_set_timer(self.source, DISPATCH_TIME_NOW, UInt64(Double(NSEC_PER_SEC) / self.rate), 0)
        dispatch_source_set_event_handler(self.source, self.tick)
    }

    func resume() {
        dispatch_resume(self.source)
    }
}

var timer = Timer()
timer.tick = {
    let now = NSDate()
    print("\(now)")
}
timer.cancel()
timer.resume()

I have no compilation errors. But the console at the bottom of the playground shows nothing. If I add

timer.tick()

It outputs the result of the current date, apparently executing tick function once. I followed Matt's example in his book pretty closely (making small changes for education purposes). So I'm left with not knowing if

A) It's just fine and will work fine when I move it to real code, it just doesn't show anywhere apparent in the Playground output B) Somethings not missing and it's not really firing

Travis Griggs
  • 21,522
  • 19
  • 91
  • 167

1 Answers1

2

Once a source has been cancelled by dispatch_source_cancel, it will never call its event handler again. You cannot reenable the source with dispatch_resume after cancelling it; you must create a new source.

To make your playground work, you must remove the call to timer.cancel().

Furthermore, after the entire playground script has been executed, the playground process exits by default. If you want it to keep running, you need to set XCPlaygroundPage.currentPage.needsIndefiniteExecution = true.

So make the end of your script look like this:

var timer = Timer()
timer.tick = {
    let now = NSDate()
    print("\(now)")
}
timer.resume()
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
rob mayoff
  • 375,296
  • 67
  • 796
  • 848