Skip to content

Instantly share code, notes, and snippets.

@natecook1000
Last active January 6, 2024 07:23
Show Gist options
  • Star 109 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save natecook1000/b0285b518576b22c4dc8 to your computer and use it in GitHub Desktop.
Save natecook1000/b0285b518576b22c4dc8 to your computer and use it in GitHub Desktop.
Scheduled NSTimer with a Swift closure
extension NSTimer {
/**
Creates and schedules a one-time `NSTimer` instance.
- Parameters:
- delay: The delay before execution.
- handler: A closure to execute after `delay`.
- Returns: The newly-created `NSTimer` instance.
*/
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
}
/**
Creates and schedules a repeating `NSTimer` instance.
- Parameters:
- repeatInterval: The interval (in seconds) between each execution of
`handler`. Note that individual calls may be delayed; subsequent calls
to `handler` will be based on the time the timer was created.
- handler: A closure to execute at each `repeatInterval`.
- Returns: The newly-created `NSTimer` instance.
*/
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
}
}
// Usage:
var count = 0
NSTimer.schedule(repeatInterval: 1) { timer in
print(++count)
if count >= 10 {
timer.invalidate()
}
}
NSTimer.schedule(delay: 5) { timer in
print("5 seconds")
}
@zwaldowski
Copy link

Consider using CFRunLoopTimerCreateWithHandler, which toll-free bridges to NSTimer.

@natecook1000
Copy link
Author

Great suggestion, thanks! Makes the wrapper around the closure completely unnecessary.

@rinatkhanov
Copy link

Just a minor styling addition, I think scheduledTimerWithTimeInterval is too long for a method name and could be easily replaced here by schedule(interval: repeats).

So I'd prefer using it like NSTimer.schedule(interval: 1, repeats: true) which is more concise, yet as clear. (more thoughts on Swift method naming here)

@natecook1000
Copy link
Author

@rinatkhanov, I like that idea too. I've split the original method into NSTimer.schedule(delay:handler:) and NSTimer.schedule(repeatInterval:handler:).

@simogerard
Copy link

how can I use this extension to reset and restart the timer every 20 seconds? The method will be inserted in a UIButton action. Thanks!

@julien-c
Copy link

julien-c commented Nov 1, 2016

This is what I'm running for Swift 3.0:

extension Timer {
    /**
    Creates and schedules a one-time `NSTimer` instance.

    :param: delay The delay before execution.
    :param: handler A closure to execute after `delay`.

    :returns: The newly-created `NSTimer` instance.
    */
    class func schedule(delay: TimeInterval, handler: @escaping (Timer?) -> Void) -> Timer {
        let fireDate = delay + CFAbsoluteTimeGetCurrent()
        let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, 0, 0, 0, handler)!
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.commonModes)
        return timer
    }

    /**
    Creates and schedules a repeating `NSTimer` instance.

    :param: repeatInterval The interval between each execution of `handler`. Note that individual calls may be delayed; subsequent calls to `handler` will be based on the time the `NSTimer` was created.
    :param: handler A closure to execute after `delay`.

    :returns: The newly-created `NSTimer` instance.
    */
    class func schedule(repeatInterval interval: TimeInterval, handler: @escaping (Timer?) -> Void) -> Timer {
        let fireDate = interval + CFAbsoluteTimeGetCurrent()
        let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, interval, 0, 0, handler)!
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, CFRunLoopMode.commonModes)
        return timer
    }
}

Also from iOS 10, there is a native method on Timer that does this: Timer.scheduledTimer(withTimeInterval: TimeInterval, repeats: Bool, block: (Timer) -> Void)

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