import Foundation public final class Debouncer { public var isCancelled: Bool { workItem.isCancelled } private lazy var queue: DispatchQueue = { DispatchQueue(label: "com.traderepublic.debouncer", target: .global()) }() private var workItem: DispatchWorkItem = .init(block: {}) private var interval: TimeInterval private var restart: Bool private var lastExecution: Date? public init(interval: TimeInterval, restart: Bool = true) { self.interval = interval self.restart = restart } public func debounce(_ action: @escaping (() -> Void)) { debounce(on: queue, action) } public func debounce(on queue: DispatchQueue, _ action: @escaping (() -> Void)) { workItem.cancel() workItem = DispatchWorkItem(block: action) let nextExecution: Date let now: Date = .init() if restart { nextExecution = now + interval } else { if let lastExecution: Date = self.lastExecution { let lastExecutionInterval: TimeInterval = now.timeIntervalSince(lastExecution) nextExecution = now + max(0, interval - lastExecutionInterval) } else { nextExecution = now } } queue.asyncAfter(deadline: .now() + nextExecution.timeIntervalSinceNow, execute: workItem) lastExecution = nextExecution } public func cancel() { workItem.cancel() } }