diff --git a/Sources/SwiftyTimer.swift b/Sources/SwiftyTimer.swift index 519e720..6f7be73 100644 --- a/Sources/SwiftyTimer.swift +++ b/Sources/SwiftyTimer.swift @@ -23,10 +23,48 @@ // import Foundation +import ObjectiveC extension Timer { -// MARK: Schedule timers + // MARK: Schedule timers + + private struct AssociatedKeys { + static var repeatCounterAddress = "repeat_counter_address" + static var isTimerRunning = "is_timer_running_address" + } + + public var isRunning: Bool { + get { + return isValid && objc_getAssociatedObject(self, &AssociatedKeys.isTimerRunning) as? Bool ?? false + } + set { + objc_setAssociatedObject(self, + &AssociatedKeys.isTimerRunning, + newValue, + .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + + private var repeatCounter: Int { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.repeatCounterAddress) as? Int ?? 0 + } + set { + objc_setAssociatedObject(self, + &AssociatedKeys.repeatCounterAddress, + newValue, + .OBJC_ASSOCIATION_RETAIN_NONATOMIC) + } + } + + public class func every(_ interval: TimeInterval, `for` times: Int, _ block: @escaping (Timer, Int) -> Void) -> Timer { + + let timer = Timer.new(every: interval, for: times, block) + timer.start() + return timer + } /// Create and schedule a timer that will call `block` once after the specified time. @@ -56,16 +94,17 @@ extension Timer { return timer } -// MARK: Create timers without scheduling + // MARK: Create timers 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: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947) - + public class func new(after interval: TimeInterval, _ block: @escaping () -> Void) -> Timer { - return CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, 0, 0, 0) { _ in + return CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, 0, 0, 0) { timer in + (timer as! Timer).isRunning = false block() } } @@ -75,13 +114,31 @@ extension Timer { /// - 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: The `new` class function is a workaround for a crashing bug when using convenience initializers (rdar://18720947) - + public class func new(every interval: TimeInterval, _ block: @escaping () -> Void) -> Timer { return CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in block() } } + @nonobjc public class func new(every interval: TimeInterval, `for` times: Int, _ block: @escaping () -> Void) -> Timer { + var timer: Timer! + + timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in + + if times > 0 { + if timer.repeatCounter == (times-1) { + timer.invalidate() + } + timer.repeatCounter += 1 + + } + + block() + } + return timer + } + /// Create a timer that will call `block` repeatedly in specified time intervals. /// (This variant also passes the timer instance to the block) /// @@ -89,6 +146,25 @@ extension Timer { /// 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: TimeInterval, `for` times: Int, _ block: @escaping (Timer, Int) -> Void) -> Timer { + var timer: Timer! + + timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in + + if times > 0 { + if timer.repeatCounter == (times-1) { + timer.invalidate() + } + + timer.repeatCounter += 1 + + } + + block(timer, timer.repeatCounter) + } + return timer + } + @nonobjc public class func new(every interval: TimeInterval, _ block: @escaping (Timer) -> Void) -> Timer { var timer: Timer! timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0) { _ in @@ -97,7 +173,7 @@ extension Timer { return timer } -// MARK: Manual scheduling + // MARK: Manual scheduling /// Schedule this timer on the run loop /// @@ -105,17 +181,19 @@ extension Timer { /// Specify `runLoop` or `modes` to override these defaults. public func start(runLoop: RunLoop = .current, modes: RunLoopMode...) { + isRunning = true let modes = modes.isEmpty ? [.defaultRunLoopMode] : modes for mode in modes { runLoop.add(self, forMode: mode) } } + } // MARK: - Time extensions -extension Double { +public extension Double { public var millisecond: TimeInterval { return self / 1000 } public var milliseconds: TimeInterval { return self / 1000 } public var ms: TimeInterval { return self / 1000 } @@ -132,3 +210,4 @@ extension Double { public var day: TimeInterval { return self * 3600 * 24 } public var days: TimeInterval { return self * 3600 * 24 } } + diff --git a/SwiftyTimer.xcodeproj/project.pbxproj b/SwiftyTimer.xcodeproj/project.pbxproj index 70f8d1b..6e4dc0d 100644 --- a/SwiftyTimer.xcodeproj/project.pbxproj +++ b/SwiftyTimer.xcodeproj/project.pbxproj @@ -205,12 +205,12 @@ 3E721AB21BF7255C008AF027 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = "Radosław Pietruszewski"; TargetAttributes = { 3E721ABA1BF7255D008AF027 = { CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 0920; }; 6E7E40891C84B1A20030CEBB = { CreatedOnToolsVersion = 7.2; @@ -318,14 +318,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -355,7 +363,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -370,14 +378,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -400,7 +416,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -425,6 +441,8 @@ PRODUCT_BUNDLE_IDENTIFIER = io.radex.SwiftyTimer; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -445,6 +463,8 @@ PRODUCT_BUNDLE_IDENTIFIER = io.radex.SwiftyTimer; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/SwiftyTimer.xcodeproj/xcshareddata/xcschemes/SwiftyTimer OS X.xcscheme b/SwiftyTimer.xcodeproj/xcshareddata/xcschemes/SwiftyTimer OS X.xcscheme index fd51c27..5570b41 100644 --- a/SwiftyTimer.xcodeproj/xcshareddata/xcschemes/SwiftyTimer OS X.xcscheme +++ b/SwiftyTimer.xcodeproj/xcshareddata/xcschemes/SwiftyTimer OS X.xcscheme @@ -1,6 +1,6 @@