Create and use PostponingBlock.

This commit is contained in:
Brent Simmons 2024-06-09 22:10:20 -07:00
parent 0d36face00
commit 74a42c6f3e
3 changed files with 76 additions and 37 deletions

View File

@ -21,20 +21,24 @@ public protocol DataFileDelegate: AnyObject {
private var isDirty = false {
didSet {
if isDirty {
restartTimer()
postponingBlock.runInFuture()
}
else {
invalidateTimer()
postponingBlock.cancelRun()
}
}
}
private let fileURL: URL
private let saveInterval: TimeInterval = 1.0
private var timer: Timer?
private lazy var postponingBlock: PostponingBlock = {
PostponingBlock(delayInterval: 1.0) { [weak self] in
self?.saveToDiskIfNeeded()
}
}()
public init(fileURL: URL) {
self.fileURL = fileURL
}
@ -64,33 +68,8 @@ private extension DataFile {
func saveToDiskIfNeeded() {
assert(Thread.isMainThread)
if isDirty {
save()
}
}
func restartTimer() {
assert(Thread.isMainThread)
invalidateTimer()
timer = Timer.scheduledTimer(withTimeInterval: saveInterval, repeats: false) { timer in
MainActor.assumeIsolated {
self.saveToDiskIfNeeded()
}
}
}
func invalidateTimer() {
assert(Thread.isMainThread)
if let timer, timer.isValid {
timer.invalidate()
}
timer = nil
}
}

View File

@ -0,0 +1,51 @@
//
// PostponingBlock.swift
//
//
// Created by Brent Simmons on 6/9/24.
//
import Foundation
/// Runs a block of code in the future. Each time `runInFuture` is called, the block is postponed again until the future by `delayInterval`.
@MainActor public final class PostponingBlock {
private let block: () -> Void
private let delayInterval: TimeInterval
private var timer: Timer?
public init(delayInterval: TimeInterval, block: @escaping () -> Void) {
self.block = block
self.delayInterval = delayInterval
}
/// Run the block in `delayInterval` seconds, canceling any run about to happen before then.
public func runInFuture() {
invalidateTimer()
timer = Timer.scheduledTimer(withTimeInterval: delayInterval, repeats: false) { timer in
MainActor.assumeIsolated {
self.block()
}
}
}
/// Cancel any upcoming run.
public func cancelRun() {
invalidateTimer()
}
}
private extension PostponingBlock {
func invalidateTimer() {
if let timer, timer.isValid {
timer.invalidate()
}
timer = nil
}
}

View File

@ -48,6 +48,15 @@ final class SmartFeed: PseudoFeed {
}
#endif
private lazy var postponingBlock: PostponingBlock = {
PostponingBlock(delayInterval: 1.0) {
Task {
try? await self.fetchUnreadCounts()
}
}
}()
private var fetchUnreadCountsTask: Task<Void, Never>?
private let delegate: SmartFeedDelegate
private var unreadCounts = [String: Int]()
@ -63,7 +72,7 @@ final class SmartFeed: PseudoFeed {
}
}
@MainActor @objc func fetchUnreadCounts() {
@MainActor func fetchUnreadCounts() async throws {
let activeAccounts = AccountManager.shared.activeAccounts
@ -79,11 +88,9 @@ final class SmartFeed: PseudoFeed {
updateUnreadCount()
return
}
Task { @MainActor in
for account in activeAccounts {
await fetchUnreadCount(for: account)
}
for account in activeAccounts {
await fetchUnreadCount(for: account)
}
}
}
@ -104,7 +111,8 @@ extension SmartFeed: ArticleFetcher {
private extension SmartFeed {
@MainActor func queueFetchUnreadCounts() {
CoalescingQueue.standard.add(self, #selector(fetchUnreadCounts))
postponingBlock.runInFuture()
}
@MainActor func fetchUnreadCount(for account: Account) async {
@ -125,3 +133,4 @@ private extension SmartFeed {
unreadCount = unread
}
}