From 07d565ccfc4c3dbbaf84915e01cd273d7858bd31 Mon Sep 17 00:00:00 2001 From: radex Date: Sun, 10 Apr 2016 14:26:32 +0200 Subject: [PATCH 1/4] =?UTF-8?q?Revert=20back=20to=20having=20a=20helper=20?= =?UTF-8?q?=E2=80=9CActor=E2=80=9D=20class=20instead=20of=20using=20CGRunL?= =?UTF-8?q?oopTimerCreateWithHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/SwiftyTimer.swift | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/Sources/SwiftyTimer.swift b/Sources/SwiftyTimer.swift index e1155c1..0972441 100644 --- a/Sources/SwiftyTimer.swift +++ b/Sources/SwiftyTimer.swift @@ -32,9 +32,8 @@ 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. @@ -44,9 +43,8 @@ extension NSTimer { /// - 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() - } + let actor = Actor { _ in block() } + return 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. @@ -57,11 +55,8 @@ extension NSTimer { /// - 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 +96,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 From 01c7b1f07109c6314a7b7b1cf4166ebf57dedbee Mon Sep 17 00:00:00 2001 From: radex Date: Sun, 10 Apr 2016 14:28:31 +0200 Subject: [PATCH 2/4] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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) From 25cf3f312727d300eb1f2cef3bdf8bbbc3bd42f5 Mon Sep 17 00:00:00 2001 From: radex Date: Sun, 10 Apr 2016 14:28:33 +0200 Subject: [PATCH 3/4] Clean up --- Sources/SwiftyTimer.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/SwiftyTimer.swift b/Sources/SwiftyTimer.swift index 0972441..49ded0d 100644 --- a/Sources/SwiftyTimer.swift +++ b/Sources/SwiftyTimer.swift @@ -43,8 +43,7 @@ extension NSTimer { /// - 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 { - let actor = Actor { _ in block() } - return self.init(timeInterval: interval, target: actor, selector: #selector(Actor.fire), userInfo: nil, repeats: true) + return self.new(every: interval) { (_: NSTimer) in block() } } /// Create a timer that will call `block` repeatedly in specified time intervals. From d11abbea8b8372b071ab25e0001d83200928c63b Mon Sep 17 00:00:00 2001 From: radex Date: Sun, 10 Apr 2016 14:49:29 +0200 Subject: [PATCH 4/4] Use convenience initializers? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doesn’t work :( --- Sources/SwiftyTimer.swift | 49 +++++++++++++++++- SwiftyTimerTests/SwiftyTimerTests/main.swift | 54 ++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftyTimer.swift b/Sources/SwiftyTimer.swift index 49ded0d..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. @@ -39,7 +84,7 @@ extension NSTimer { /// 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 { @@ -50,7 +95,7 @@ extension NSTimer { /// (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 { 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() {