mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-15 00:34:08 +09:00
move from own implementation of Repeat to the 3d-party library
This commit is contained in:
3
Cartfile
3
Cartfile
@@ -1,2 +1,3 @@
|
||||
github "danielgindi/Charts" ~> 3.4.0
|
||||
github "ashleymills/Reachability.swift" ~> 5.0.0
|
||||
github "ashleymills/Reachability.swift" ~> 5.0.0
|
||||
github "malcommac/Repeat" ~> 0.6.0
|
||||
@@ -1,2 +1,3 @@
|
||||
github "ashleymills/Reachability.swift" "v5.0.0"
|
||||
github "danielgindi/Charts" "v3.4.0"
|
||||
github "danielgindi/Charts" "v3.5.0"
|
||||
github "malcommac/Repeat" "0.6.0"
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
9A09C8A222B3D94D0018426F /* BatteryWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A09C8A122B3D94D0018426F /* BatteryWidget.swift */; };
|
||||
9A1410F9229E721100D29793 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A1410F8229E721100D29793 /* AppDelegate.swift */; };
|
||||
9A141100229E721200D29793 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9A1410FE229E721200D29793 /* Main.storyboard */; };
|
||||
9A2D15D023C77BA300C4C417 /* Repeater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2D15CF23C77BA300C4C417 /* Repeater.swift */; };
|
||||
9A2D15D223CCEC7600C4C417 /* Module.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2D15D123CCEC7600C4C417 /* Module.swift */; };
|
||||
9A2D15D523CCEFF700C4C417 /* CPU.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2D15D423CCEFF700C4C417 /* CPU.swift */; };
|
||||
9A2D15D723CCFE1B00C4C417 /* CPULoadReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A2D15D623CCFE1B00C4C417 /* CPULoadReader.swift */; };
|
||||
@@ -53,6 +52,8 @@
|
||||
9A6CFC0122A1C9F5001E782D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9A6CFC0022A1C9F5001E782D /* Assets.xcassets */; };
|
||||
9A74D59722B44498004FE1FA /* Mini.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A74D59622B44498004FE1FA /* Mini.swift */; };
|
||||
9A79B36A22D3BEE600BF1C3A /* Widget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A79B36922D3BEE600BF1C3A /* Widget.swift */; };
|
||||
9A7DE036245982460084BD7A /* Repeat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A7DE035245982460084BD7A /* Repeat.framework */; };
|
||||
9A7DE037245982460084BD7A /* Repeat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A7DE035245982460084BD7A /* Repeat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9AA28DC1243774ED00D2B196 /* Sensors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DC0243774ED00D2B196 /* Sensors.swift */; };
|
||||
9AA28DC32437752D00D2B196 /* SensorsMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DC22437752D00D2B196 /* SensorsMenu.swift */; };
|
||||
9AA28DD1243799E500D2B196 /* SensorsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AA28DD0243799E500D2B196 /* SensorsWidget.swift */; };
|
||||
@@ -78,6 +79,7 @@
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
9A5349CF23D8832E00C23824 /* Reachability.framework in Embed Frameworks */,
|
||||
9A7DE037245982460084BD7A /* Repeat.framework in Embed Frameworks */,
|
||||
9A6698E62326AB16001D00E1 /* Charts.framework in Embed Frameworks */,
|
||||
);
|
||||
name = "Embed Frameworks";
|
||||
@@ -102,7 +104,6 @@
|
||||
9A1410F8229E721100D29793 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
9A1410FF229E721200D29793 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
9A141101229E721200D29793 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
9A2D15CF23C77BA300C4C417 /* Repeater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Repeater.swift; sourceTree = "<group>"; };
|
||||
9A2D15D123CCEC7600C4C417 /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = "<group>"; };
|
||||
9A2D15D423CCEFF700C4C417 /* CPU.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPU.swift; sourceTree = "<group>"; };
|
||||
9A2D15D623CCFE1B00C4C417 /* CPULoadReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CPULoadReader.swift; sourceTree = "<group>"; };
|
||||
@@ -146,6 +147,7 @@
|
||||
9A6CFC0022A1C9F5001E782D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
9A74D59622B44498004FE1FA /* Mini.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mini.swift; sourceTree = "<group>"; };
|
||||
9A79B36922D3BEE600BF1C3A /* Widget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widget.swift; sourceTree = "<group>"; };
|
||||
9A7DE035245982460084BD7A /* Repeat.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Repeat.framework; path = Carthage/Build/Mac/Repeat.framework; sourceTree = "<group>"; };
|
||||
9A998CD722A199920087ADE7 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
9A998CD922A199970087ADE7 /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
|
||||
9AA28DC0243774ED00D2B196 /* Sensors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sensors.swift; sourceTree = "<group>"; };
|
||||
@@ -171,6 +173,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9A5349CE23D8832E00C23824 /* Reachability.framework in Frameworks */,
|
||||
9A7DE036245982460084BD7A /* Repeat.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -328,7 +331,6 @@
|
||||
9A5B1CC4229E7B40008B9D3C /* Extensions.swift */,
|
||||
9A426DB722C2B5EE00C064C4 /* MacAppUpdater.swift */,
|
||||
9A59AE55231EE02F007989D6 /* ChartMarker.swift */,
|
||||
9A2D15CF23C77BA300C4C417 /* Repeater.swift */,
|
||||
9A3434F0243E19E6006B19F9 /* LaunchAtLogin.swift */,
|
||||
);
|
||||
path = libs;
|
||||
@@ -350,6 +352,7 @@
|
||||
9A998CD622A199920087ADE7 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A7DE035245982460084BD7A /* Repeat.framework */,
|
||||
9A5349CD23D8832E00C23824 /* Reachability.framework */,
|
||||
9A6698E32326AAE5001D00E1 /* Charts.framework */,
|
||||
9A998CD922A199970087ADE7 /* ServiceManagement.framework */,
|
||||
@@ -564,7 +567,6 @@
|
||||
9A57A18522A1D26D0033E318 /* MenuBar.swift in Sources */,
|
||||
9A2D15D923CD036400C4C417 /* CPUMenu.swift in Sources */,
|
||||
9AF6F1FE231D732600B8E1E4 /* PopupViewController.swift in Sources */,
|
||||
9A2D15D023C77BA300C4C417 /* Repeater.swift in Sources */,
|
||||
9A1410F9229E721100D29793 /* AppDelegate.swift in Sources */,
|
||||
9A2D15E823CE29A400C4C417 /* RAMMenu.swift in Sources */,
|
||||
9A2D15DB23CD0B2100C4C417 /* CPUPopup.swift in Sources */,
|
||||
@@ -756,7 +758,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MARKETING_VERSION = 1.6.2;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -787,7 +789,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MARKETING_VERSION = 1.6.2;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = eu.exelban.Stats;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Cocoa
|
||||
import IOKit.ps
|
||||
import Repeat
|
||||
|
||||
class Battery: Module {
|
||||
public var name: String = "Battery"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Cocoa
|
||||
import Charts
|
||||
import Repeat
|
||||
|
||||
class CPU: Module {
|
||||
public var name: String = "CPU"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Repeat
|
||||
|
||||
class Disk: Module {
|
||||
public var name: String = "SSD"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Cocoa
|
||||
import Charts
|
||||
import Repeat
|
||||
|
||||
protocol Module: class {
|
||||
var name: String { get } // module name
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Cocoa
|
||||
import Charts
|
||||
import Repeat
|
||||
|
||||
class Network: Module {
|
||||
public var name: String = "Network"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Cocoa
|
||||
import Charts
|
||||
import Repeat
|
||||
|
||||
class RAM: Module {
|
||||
public var name: String = "RAM"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import Repeat
|
||||
|
||||
class Sensors: Module {
|
||||
public var name: String = "Sensors"
|
||||
|
||||
@@ -1,430 +0,0 @@
|
||||
//
|
||||
// Repeat
|
||||
// A modern alternative to NSTimer made in GCD with debouncer and throttle
|
||||
// -----------------------------------------------------------------------
|
||||
// Created by: Daniele Margutti
|
||||
// hello@danielemargutti.com
|
||||
// http://www.danielemargutti.com
|
||||
//
|
||||
// Twitter: @danielemargutti
|
||||
//
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
|
||||
open class Repeater: Equatable {
|
||||
|
||||
/// State of the timer
|
||||
///
|
||||
/// - paused: idle (never started yet or paused)
|
||||
/// - running: timer is running
|
||||
/// - executing: the observers are being executed
|
||||
/// - finished: timer lifetime is finished
|
||||
public enum State: Equatable, CustomStringConvertible {
|
||||
case paused
|
||||
case running
|
||||
case executing
|
||||
case finished
|
||||
|
||||
public static func == (lhs: State, rhs: State) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.paused, .paused),
|
||||
(.running, .running),
|
||||
(.executing, .executing),
|
||||
(.finished, .finished):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if timer is currently running, including when the observers are being executed.
|
||||
public var isRunning: Bool {
|
||||
guard self == .running || self == .executing else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
/// Return `true` if the observers are being executed.
|
||||
public var isExecuting: Bool {
|
||||
guard case .executing = self else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
/// Is timer finished its lifetime?
|
||||
/// It return always `false` for infinite timers.
|
||||
/// It return `true` for `.once` mode timer after the first fire,
|
||||
/// and when `.remainingIterations` is zero for `.finite` mode timers
|
||||
public var isFinished: Bool {
|
||||
guard case .finished = self else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
/// State description
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .paused: return "idle/paused"
|
||||
case .finished: return "finished"
|
||||
case .running: return "running"
|
||||
case .executing: return "executing"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Repeat interval
|
||||
public enum Interval {
|
||||
case nanoseconds(_: Int)
|
||||
case microseconds(_: Int)
|
||||
case milliseconds(_: Int)
|
||||
case minutes(_: Int)
|
||||
case seconds(_: Double)
|
||||
case hours(_: Int)
|
||||
case days(_: Int)
|
||||
|
||||
internal var value: DispatchTimeInterval {
|
||||
switch self {
|
||||
case .nanoseconds(let value): return .nanoseconds(value)
|
||||
case .microseconds(let value): return .microseconds(value)
|
||||
case .milliseconds(let value): return .milliseconds(value)
|
||||
case .seconds(let value): return .milliseconds(Int( Double(value) * Double(1000)))
|
||||
case .minutes(let value): return .seconds(value * 60)
|
||||
case .hours(let value): return .seconds(value * 3600)
|
||||
case .days(let value): return .seconds(value * 86400)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mode of the timer.
|
||||
///
|
||||
/// - infinite: infinite number of repeats.
|
||||
/// - finite: finite number of repeats.
|
||||
/// - once: single repeat.
|
||||
public enum Mode {
|
||||
case infinite
|
||||
case finite(_: Int)
|
||||
case once
|
||||
|
||||
/// Is timer a repeating timer?
|
||||
internal var isRepeating: Bool {
|
||||
switch self {
|
||||
case .once: return false
|
||||
default: return true
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of repeats, if applicable. Otherwise `nil`
|
||||
public var countIterations: Int? {
|
||||
switch self {
|
||||
case .finite(let counts): return counts
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Is infinite timer
|
||||
public var isInfinite: Bool {
|
||||
guard case .infinite = self else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Handler typealias
|
||||
public typealias Observer = ((Repeater) -> Void)
|
||||
|
||||
/// Token assigned to the observer
|
||||
public typealias ObserverToken = UInt64
|
||||
|
||||
/// Current state of the timer
|
||||
public private(set) var state: State = .paused {
|
||||
didSet {
|
||||
self.onStateChanged?(self, state)
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback called to intercept state's change of the timer
|
||||
public var onStateChanged: ((_ timer: Repeater, _ state: State) -> Void)?
|
||||
|
||||
/// List of the observer of the timer
|
||||
private var observers = [ObserverToken: Observer]()
|
||||
|
||||
/// Next token of the timer
|
||||
private var nextObserverID: UInt64 = 0
|
||||
|
||||
/// Internal GCD Timer
|
||||
private var timer: DispatchSourceTimer?
|
||||
|
||||
/// Is timer a repeat timer
|
||||
public private(set) var mode: Mode
|
||||
|
||||
/// Number of remaining repeats count
|
||||
public private(set) var remainingIterations: Int?
|
||||
|
||||
/// Interval of the timer
|
||||
private var interval: Interval
|
||||
|
||||
/// Accuracy of the timer
|
||||
private var tolerance: DispatchTimeInterval
|
||||
|
||||
/// Dispatch queue parent of the timer
|
||||
private var queue: DispatchQueue?
|
||||
|
||||
/// Initialize a new timer.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - interval: interval of the timer
|
||||
/// - mode: mode of the timer
|
||||
/// - tolerance: tolerance of the timer, 0 is default.
|
||||
/// - queue: queue in which the timer should be executed; if `nil` a new queue is created automatically.
|
||||
/// - observer: observer
|
||||
public init(interval: Interval, mode: Mode = .infinite, tolerance: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue? = nil, observer: @escaping Observer) {
|
||||
self.mode = mode
|
||||
self.interval = interval
|
||||
self.tolerance = tolerance
|
||||
self.remainingIterations = mode.countIterations
|
||||
self.queue = (queue ?? DispatchQueue(label: "com.repeat.queue"))
|
||||
self.timer = configureTimer()
|
||||
self.observe(observer)
|
||||
}
|
||||
|
||||
/// Add new a listener to the timer.
|
||||
///
|
||||
/// - Parameter callback: callback to call for fire events.
|
||||
/// - Returns: token used to remove the handler
|
||||
@discardableResult
|
||||
public func observe(_ observer: @escaping Observer) -> ObserverToken {
|
||||
var (new, overflow) = self.nextObserverID.addingReportingOverflow(1)
|
||||
if overflow { // you need to add an incredible number of offset...sure you can't
|
||||
self.nextObserverID = 0
|
||||
new = 0
|
||||
}
|
||||
self.nextObserverID = new
|
||||
self.observers[new] = observer
|
||||
return new
|
||||
}
|
||||
|
||||
/// Remove an observer of the timer.
|
||||
///
|
||||
/// - Parameter id: id of the observer to remove
|
||||
public func remove(observer identifier: ObserverToken) {
|
||||
self.observers.removeValue(forKey: identifier)
|
||||
}
|
||||
|
||||
/// Remove all observers of the timer.
|
||||
///
|
||||
/// - Parameter stopTimer: `true` to also stop timer by calling `pause()` function.
|
||||
public func removeAllObservers(thenStop stopTimer: Bool = false) {
|
||||
self.observers.removeAll()
|
||||
|
||||
if stopTimer {
|
||||
self.pause()
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure a new timer session.
|
||||
///
|
||||
/// - Returns: dispatch timer
|
||||
private func configureTimer() -> DispatchSourceTimer {
|
||||
let associatedQueue = (queue ?? DispatchQueue(label: "com.repeat.\(NSUUID().uuidString)"))
|
||||
let timer = DispatchSource.makeTimerSource(queue: associatedQueue)
|
||||
let repeatInterval = interval.value
|
||||
let deadline: DispatchTime = (DispatchTime.now() + repeatInterval)
|
||||
if self.mode.isRepeating {
|
||||
timer.schedule(deadline: deadline, repeating: repeatInterval, leeway: tolerance)
|
||||
} else {
|
||||
timer.schedule(deadline: deadline, leeway: tolerance)
|
||||
}
|
||||
|
||||
timer.setEventHandler { [weak self] in
|
||||
if let unwrapped = self {
|
||||
unwrapped.timeFired()
|
||||
}
|
||||
}
|
||||
return timer
|
||||
}
|
||||
|
||||
/// Destroy current timer
|
||||
private func destroyTimer() {
|
||||
self.timer?.setEventHandler(handler: nil)
|
||||
self.timer?.cancel()
|
||||
|
||||
if state == .paused || state == .finished {
|
||||
self.timer?.resume()
|
||||
}
|
||||
}
|
||||
|
||||
/// Create and schedule a timer that will call `handler` once after the specified time.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - interval: interval delay for single fire
|
||||
/// - queue: destination queue, if `nil` a new `DispatchQueue` is created automatically.
|
||||
/// - observer: handler to call when timer fires.
|
||||
/// - Returns: timer instance
|
||||
@discardableResult
|
||||
public class func once(after interval: Interval, tolerance: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue? = nil, _ observer: @escaping Observer) -> Repeater {
|
||||
let timer = Repeater(interval: interval, mode: .once, tolerance: tolerance, queue: queue, observer: observer)
|
||||
timer.start()
|
||||
return timer
|
||||
}
|
||||
|
||||
/// Create and schedule a timer that will fire every interval optionally by limiting the number of fires.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - interval: interval of fire
|
||||
/// - count: a non `nil` and > 0 value to limit the number of fire, `nil` to set it as infinite.
|
||||
/// - queue: destination queue, if `nil` a new `DispatchQueue` is created automatically.
|
||||
/// - handler: handler to call on fire
|
||||
/// - Returns: timer
|
||||
@discardableResult
|
||||
public class func every(_ interval: Interval, count: Int? = nil, tolerance: DispatchTimeInterval = .nanoseconds(0), queue: DispatchQueue? = nil, _ handler: @escaping Observer) -> Repeater {
|
||||
let mode: Mode = (count != nil ? .finite(count!) : .infinite)
|
||||
let timer = Repeater(interval: interval, mode: mode, tolerance: tolerance, queue: queue, observer: handler)
|
||||
timer.start()
|
||||
return timer
|
||||
}
|
||||
|
||||
/// Force fire.
|
||||
///
|
||||
/// - Parameter pause: `true` to pause after fire, `false` to continue the regular firing schedule.
|
||||
public func fire(andPause pause: Bool = false) {
|
||||
self.timeFired()
|
||||
if pause == true {
|
||||
self.pause()
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the state of the timer, optionally changing the fire interval.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - interval: new fire interval; pass `nil` to keep the latest interval set.
|
||||
/// - restart: `true` to automatically restart the timer, `false` to keep it stopped after configuration.
|
||||
public func reset(_ interval: Interval?, restart: Bool = true) {
|
||||
if self.state.isRunning {
|
||||
self.setPause(from: self.state)
|
||||
}
|
||||
|
||||
// For finite counter we want to also reset the repeat count
|
||||
if case .finite(let count) = self.mode {
|
||||
self.remainingIterations = count
|
||||
}
|
||||
|
||||
// Create a new instance of timer configured
|
||||
if let newInterval = interval {
|
||||
self.interval = newInterval
|
||||
} // update interval
|
||||
self.destroyTimer()
|
||||
self.timer = configureTimer()
|
||||
self.state = .paused
|
||||
|
||||
if restart {
|
||||
self.timer?.resume()
|
||||
self.state = .running
|
||||
}
|
||||
}
|
||||
|
||||
/// Start timer. If timer is already running it does nothing.
|
||||
@discardableResult
|
||||
public func start() -> Bool {
|
||||
guard self.state.isRunning == false else {
|
||||
return false
|
||||
}
|
||||
|
||||
// If timer has not finished its lifetime we want simply
|
||||
// restart it from the current state.
|
||||
guard self.state.isFinished == true else {
|
||||
self.state = .running
|
||||
self.timer?.resume()
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise we need to reset the state based upon the mode
|
||||
// and start it again.
|
||||
self.reset(nil, restart: true)
|
||||
return true
|
||||
}
|
||||
|
||||
/// Pause a running timer. If timer is paused it does nothing.
|
||||
@discardableResult
|
||||
public func pause() -> Bool {
|
||||
guard state != .paused && state != .finished else {
|
||||
return false
|
||||
}
|
||||
|
||||
return self.setPause(from: self.state)
|
||||
}
|
||||
|
||||
/// Pause a running timer optionally changing the state with regard to the current state.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - from: the state which the timer should only be paused if it is the current state
|
||||
/// - to: the new state to change to if the timer is paused
|
||||
/// - Returns: `true` if timer is paused
|
||||
@discardableResult
|
||||
private func setPause(from currentState: State, to newState: State = .paused) -> Bool {
|
||||
guard self.state == currentState else {
|
||||
return false
|
||||
}
|
||||
|
||||
self.timer?.suspend()
|
||||
self.state = newState
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Called when timer is fired
|
||||
private func timeFired() {
|
||||
self.state = .executing
|
||||
|
||||
if case .finite = self.mode {
|
||||
self.remainingIterations! -= 1
|
||||
}
|
||||
|
||||
// dispatch to observers
|
||||
self.observers.values.forEach { $0(self) }
|
||||
|
||||
// manage lifetime
|
||||
switch self.mode {
|
||||
case .once:
|
||||
// once timer's lifetime is finished after the first fire
|
||||
// you can reset it by calling `reset()` function.
|
||||
self.setPause(from: .executing, to: .finished)
|
||||
case .finite:
|
||||
// for finite intervals we decrement the left iterations count...
|
||||
if self.remainingIterations! == 0 {
|
||||
// ...if left count is zero we just pause the timer and stop
|
||||
self.setPause(from: .executing, to: .finished)
|
||||
}
|
||||
case .infinite:
|
||||
// infinite timer does nothing special on the state machine
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.observers.removeAll()
|
||||
self.destroyTimer()
|
||||
}
|
||||
|
||||
public static func == (lhs: Repeater, rhs: Repeater) -> Bool {
|
||||
return lhs === rhs
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user