Skip to content

Instantly share code, notes, and snippets.

@JadenGeller
Last active September 2, 2016 17:12
Show Gist options
  • Save JadenGeller/583214332eda1829b203fe618e0c8251 to your computer and use it in GitHub Desktop.
Save JadenGeller/583214332eda1829b203fe618e0c8251 to your computer and use it in GitHub Desktop.
Clock Controller
import Foundation
// Controller that evokes its callback every minute
class ClockController {
private let minuteCallback: NSDate -> ()
init(minuteCallback: NSDate -> ()) {
self.minuteCallback = minuteCallback
callbackAndReschedule()
}
func callbackAndReschedule() {
let now = NSDate()
minuteCallback(now)
schedule(nextMinuteAfter: now)
}
func schedule(nextMinuteAfter date: NSDate) {
var spec = timespec(tv_sec: Int(date.timeIntervalSince1970), tv_nsec: 0)
// Adjust the time for exactly on the next minute
spec.tv_sec -= spec.tv_sec % 60
spec.tv_sec += 60
let time = withUnsafePointer(&spec) { dispatch_walltime($0, 0) }
dispatch_after(time, dispatch_get_main_queue()) { [weak self] in
self?.callbackAndReschedule()
}
}
}
let controller = ClockController { date in
print("Hi \(date)") // Will run on init and on the minute, every minute thereafter
}
// If the object is deallocated, the callback will no longer be called.
// So keep a strong reference to the object, and cancel it by deallocating it.
dispatch_main()
@JadenGeller
Copy link
Author

Note that this doesn't handle changes to the system clock. We can handle that by adding an observer to the NSSystemClockDidChangeNotification notification in init (and removing the observer in dealloc). When we receive a notification, we should cancel our last dispatch and schedule a new one. Note that dispatch_after does not support canceling blocks, so you'll have to use the lower level dispatch source API.

@randomer
Copy link

So why not just use an NSTimer?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment