mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
109 lines
2.5 KiB
Swift
109 lines
2.5 KiB
Swift
//
|
||
// CoalescingQueue.swift
|
||
// RSCore
|
||
//
|
||
// Created by Brent Simmons on 2/17/18.
|
||
// Copyright © 2018 Ranchero Software, LLC. All rights reserved.
|
||
//
|
||
|
||
import Foundation
|
||
import os
|
||
|
||
// Use when you want to coalesce calls for something like updating visible table cells.
|
||
// Calls are uniqued. If you add a call with the same target and selector as a previous call, you’ll just get one call.
|
||
// Targets are weakly-held. If a target goes to nil, the call is not performed.
|
||
// The perform date is pushed off every time a call is added.
|
||
// Calls are FIFO.
|
||
|
||
struct QueueCall: Equatable {
|
||
|
||
weak var target: AnyObject?
|
||
let selector: Selector
|
||
|
||
func perform() {
|
||
|
||
let _ = target?.perform(selector)
|
||
}
|
||
|
||
static func ==(lhs: QueueCall, rhs: QueueCall) -> Bool {
|
||
|
||
return lhs.target === rhs.target && lhs.selector == rhs.selector
|
||
}
|
||
}
|
||
|
||
@MainActor @objc public final class CoalescingQueue: NSObject {
|
||
|
||
@MainActor public static let standard = CoalescingQueue(name: "Standard", interval: 0.05)
|
||
public let name: String
|
||
private let interval: TimeInterval
|
||
private var lastCallTime = Date.distantFuture
|
||
private var timer: Timer? = nil
|
||
private var calls = [QueueCall]()
|
||
|
||
private var loggingEnabled = false
|
||
private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "CoalescingQueue")
|
||
private var logger: Logger {
|
||
Self.logger
|
||
}
|
||
|
||
public init(name: String, interval: TimeInterval = 0.05) {
|
||
self.name = name
|
||
self.interval = interval
|
||
}
|
||
|
||
public func add(_ target: AnyObject, _ selector: Selector) {
|
||
let queueCall = QueueCall(target: target, selector: selector)
|
||
add(queueCall)
|
||
}
|
||
|
||
public func performCallsImmediately() {
|
||
|
||
if loggingEnabled {
|
||
logger.debug("CoalescingQueue performing calls: \(self.name)")
|
||
}
|
||
|
||
let callsToMake = calls // Make a copy in case calls are added to the queue while performing calls.
|
||
resetCalls()
|
||
for call in callsToMake {
|
||
call.perform()
|
||
}
|
||
}
|
||
|
||
@objc func timerDidFire(_ sender: Any?) {
|
||
lastCallTime = Date()
|
||
performCallsImmediately()
|
||
}
|
||
}
|
||
|
||
private extension CoalescingQueue {
|
||
|
||
func add(_ call: QueueCall) {
|
||
|
||
if loggingEnabled {
|
||
logger.debug("CoalescingQueue adding to queue: \(self.name)")
|
||
}
|
||
|
||
restartTimer()
|
||
|
||
if !calls.contains(call) {
|
||
calls.append(call)
|
||
}
|
||
}
|
||
|
||
func resetCalls() {
|
||
calls = [QueueCall]()
|
||
}
|
||
|
||
func restartTimer() {
|
||
invalidateTimer()
|
||
timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(timerDidFire(_:)), userInfo: nil, repeats: false)
|
||
}
|
||
|
||
func invalidateTimer() {
|
||
if let timer = timer, timer.isValid {
|
||
timer.invalidate()
|
||
}
|
||
timer = nil
|
||
}
|
||
}
|