Skip to content

Instantly share code, notes, and snippets.

@wotjd
Forked from danielgalasko/RepeatingTimer.swift
Last active April 3, 2020 14:47
Show Gist options
  • Save wotjd/b94871dc2ceaa161ce569b4931b250cf to your computer and use it in GitHub Desktop.
Save wotjd/b94871dc2ceaa161ce569b4931b250cf to your computer and use it in GitHub Desktop.
A repeating GCD timer that can run on a background queue
import Foundation
/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
/// crashes that occur from calling resume multiple times on a timer that is
/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52
class RepeatingTimer {
// MARK: State
private enum State {
case suspended
case resumed
}
// MARK: Properties
let timeInterval: TimeInterval
var eventHandler: (() -> Void)?
private var state: State = .suspended
private lazy var timer: DispatchSourceTimer = {
let t = DispatchSource.makeTimerSource()
t.schedule(
deadline: .now() + self.timeInterval,
repeating: self.timeInterval
)
t.setEventHandler(
handler: { [weak self] in self?.eventHandler?() }
)
return t
}()
// MARK: Initializer
init(timeInterval: TimeInterval, eventHandler: (() -> Void)?) {
self.timeInterval = timeInterval
self.eventHandler = eventHandler
}
deinit {
self.timer.setEventHandler {}
self.timer.cancel()
/*
If the timer is suspended, calling cancel without resuming
triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
*/
self.resume()
self.eventHandler = nil
}
func resume() {
guard self.state != .resumed { return }
self.state = .resumed
self.timer.resume()
}
func suspend() {
guard self.state != .suspended { return }
self.state = .suspended
self.timer.suspend()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment