5

I'd like to be able to schedule a closure to be run at either an absolute or relative time in the future. I see that I can use NSTimer to schedule a selector to be called later, but this is not what I want. I would prefer to see something like this:

let timer = NSTimer.scheduleWithTimeInterval(
    ti: NSTimerInterval(1.0),
    action: {
        // do something
    }
)

Is there a built-in way to do something like this in Swift?

edit

I've now come across dispatch_after which seems to be more in line with what I want, but I'm open to other ideas.

cwharris
  • 16,958
  • 4
  • 42
  • 62
  • And see my answer here for how to make `dispatch_after` a lot more convenient to use in Swift: http://stackoverflow.com/a/24318861/341994 – matt Jun 20 '14 at 02:04

2 Answers2

4

dispatch_after should be a fine solution since there are no block based NSTimer methods.

Alternatively you could use (or create) a simple block based NSTimer category (or an extension in Swift): https://github.com/jivadevoe/NSTimer-Blocks

bjtitus
  • 4,121
  • 1
  • 25
  • 35
  • I'm interested in learning more about scheduling techniques in Objective-C / Swift / iOS. Would you happen to know of any resource to check out? – cwharris Jun 18 '14 at 20:09
  • There are in fact "block based timer methods". Not NSTimer, but dispatch-based timer. However, that's best for a repeating timer; for a one-off like this, the OP should use `dispatch_after`. – matt Jun 20 '14 at 02:03
4

I was looking for the same thing. I found this Github Gist by Nate Cook, which is an NSTimer extension that allows you to pass in a closure. The following code is copied from that Gist with the comments removed. See the link above for the full and/or updated version.

extension NSTimer {

    class func schedule(delay delay: NSTimeInterval, handler: NSTimer! -> Void) -> NSTimer {
        let fireDate = delay + CFAbsoluteTimeGetCurrent()
        let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, 0, 0, 0, handler)
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes)
        return timer
    }

    class func schedule(repeatInterval interval: NSTimeInterval, handler: NSTimer! -> Void) -> NSTimer {
        let fireDate = interval + CFAbsoluteTimeGetCurrent()
        let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, interval, 0, 0, handler)
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes)
        return timer
    }
}

The first function allows you to schedule a delayed event. The second allows you to schedule a repeated event.

Usage:

var count = 0
NSTimer.schedule(repeatInterval: 1) { timer in
    count += 1
    print(count)
    if count >= 10 {
        timer.invalidate()
    }
}

NSTimer.schedule(delay: 5) { timer in
    print("5 seconds")
}

(I modified the print(++count) line in the original since ++ is deprecated now.)

Suragch
  • 364,799
  • 232
  • 1,155
  • 1,198