diff --git a/CHANGELOG.md b/CHANGELOG.md index e2fb28f..63af2c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ### master +- Swift 2.2 now required +- Added a variant of `every` and `new(every:)` that takes a closure with `NSTimer` passed in - Fix Carthage support for Mac (set deployment target to 10.9) -- Add a variant of `every` and `new(every:)` that takes a closure with `NSTimer` passed in ### 1.3.1 (2016-03-02) diff --git a/Sources/SwiftyTimer.swift b/Sources/SwiftyTimer.swift index e1155c1..f86a9ae 100644 --- a/Sources/SwiftyTimer.swift +++ b/Sources/SwiftyTimer.swift @@ -25,6 +25,51 @@ import Foundation extension NSTimer { + +// MARK: Create a timer without scheduling + + /// Create a timer that will call `block` once after the specified time. + /// + /// - Note: The timer won't fire until it's scheduled on the run loop. + /// Use `NSTimer.after` to create and schedule a timer in one step. + /// - Note: If you support iOS 8 or older, or OS X 10.11 or older, use `NSTimer.new(after:)` + /// instead of this initializer. + + @available(iOS 9, OSX 10.11, watchOS 2, tvOS 9, *) + public convenience init(after interval: NSTimeInterval, _ block: () -> Void) { + let actor = Actor { _ in block() } + self.init(timeInterval: interval, target: actor, selector: #selector(Actor.fire), userInfo: nil, repeats: false) + } + + /// Create a timer that will call `block` repeatedly in specified time intervals. + /// + /// - Note: The timer won't fire until it's scheduled on the run loop. + /// Use `NSTimer.every` to create and schedule a timer in one step. + /// - Note: If you support iOS 8 or older, or OS X 10.11 or older, use `NSTimer.new(every:)` + /// instead of this initializer. + + @available(iOS 9, OSX 10.11, watchOS 2, tvOS 9, *) + public convenience init(every interval: NSTimeInterval, _ block: () -> Void) { + let actor = Actor { _ in block() } + self.init(timeInterval: interval, target: actor, selector: #selector(Actor.fire), userInfo: nil, repeats: true) + } + + /// Create a timer that will call `block` repeatedly in specified time intervals. + /// (This variant also passes the timer instance to the block) + /// + /// - Note: The timer won't fire until it's scheduled on the run loop. + /// Use `NSTimer.every` to create and schedule a timer in one step. + /// - Note: If you support iOS 8 or older, or OS X 10.11 or older, use `NSTimer.new(every:)` + /// instead of this initializer. + + @available(iOS 9, OSX 10.11, watchOS 2, tvOS 9, *) + @nonobjc public convenience init(every interval: NSTimeInterval, _ block: NSTimer -> Void) { + let actor = Actor(block) + self.init(timeInterval: interval, target: actor, selector: #selector(Actor.fire), userInfo: nil, repeats: true) + } + +// MARK: (Legacy factory methods) + /// Create a timer that will call `block` once after the specified time. /// /// - Note: The timer won't fire until it's scheduled on the run loop. @@ -32,36 +77,30 @@ extension NSTimer { /// - Note: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947) public class func new(after interval: NSTimeInterval, _ block: () -> Void) -> NSTimer { - return CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, 0, 0, 0) { _ in - block() - } + let actor = Actor { _ in block() } + return self.init(timeInterval: interval, target: actor, selector: #selector(Actor.fire), userInfo: nil, repeats: false) } /// Create a timer that will call `block` repeatedly in specified time intervals. /// /// - Note: The timer won't fire until it's scheduled on the run loop. - /// Use `NSTimer.after` to create and schedule a timer in one step. + /// Use `NSTimer.every` to create and schedule a timer in one step. /// - Note: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947) public class func new(every interval: NSTimeInterval, _ block: () -> Void) -> NSTimer { - return CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in - block() - } + return self.new(every: interval) { (_: NSTimer) in block() } } /// Create a timer that will call `block` repeatedly in specified time intervals. /// (This variant also passes the timer instance to the block) /// /// - Note: The timer won't fire until it's scheduled on the run loop. - /// Use `NSTimer.after` to create and schedule a timer in one step. + /// Use `NSTimer.every` to create and schedule a timer in one step. /// - Note: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947) @nonobjc public class func new(every interval: NSTimeInterval, _ block: NSTimer -> Void) -> NSTimer { - var timer: NSTimer! - timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in - block(timer) - } - return timer + let actor = Actor(block) + return self.init(timeInterval: interval, target: actor, selector: #selector(Actor.fire), userInfo: nil, repeats: true) } /// Create and schedule a timer that will call `block` once after the specified time. @@ -101,6 +140,20 @@ extension NSTimer { runLoop.addTimer(self, forMode: mode) } } + +// MARK: - Internals + + private class Actor { + var block: NSTimer -> Void + + init(_ block: NSTimer -> Void) { + self.block = block + } + + @objc func fire(timer: NSTimer) { + block(timer) + } + } } // MARK: - Time extensions diff --git a/SwiftyTimerTests/SwiftyTimerTests/main.swift b/SwiftyTimerTests/SwiftyTimerTests/main.swift index ff89ed1..966bc6b 100644 --- a/SwiftyTimerTests/SwiftyTimerTests/main.swift +++ b/SwiftyTimerTests/SwiftyTimerTests/main.swift @@ -81,6 +81,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { NSTimer.after(0.1.seconds, test8) } + // repeats with NSTimer passed in + func test8() { var fires = 0 let timer = NSTimer.new(every: 0.1.seconds) { (timer: NSTimer) in @@ -101,11 +103,63 @@ class AppDelegate: NSObject, NSApplicationDelegate { guard fires <= 1 else { fatalError("should be invalidated") } defer { fires += 1 } + if fires == 1 { + timer.invalidate() + + if #available(OSX 10.11, *) { + self.test10() + } else { + self.done() + } + } + } + } + + // init() syntax + + @available(OSX 10.11, *) + func test10() { + var fired = false + let timer = NSTimer(after: 0.1.seconds) { + guard !fired else { fatalError("should only be called once") } + defer { fired = true } + + self.test11() + fired = true + } + + timer.start() + } + + @available(OSX 10.11, *) + func test11() { + var fires = 0 + var timer: NSTimer! + timer = NSTimer(every: 0.1.seconds) { + guard fires <= 1 else { fatalError("should be invalidated") } + defer { fires += 1 } + + if fires == 1 { + timer.invalidate() + self.test12() + } + } + timer.start() + } + + @available(OSX 10.11, *) + func test12() { + var fires = 0 + let timer = NSTimer(every: 0.1.seconds) { (timer: NSTimer) in + guard fires <= 1 else { fatalError("should be invalidated") } + defer { fires += 1 } + if fires == 1 { timer.invalidate() self.done() } } + timer.start() } func done() {