crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 80a2bd8a09c436809578781aa2ab032693251e45
parent 154b90e09beea5f7c8671846f1ffb8a63ab983e2
Author: Michael Camilleri <[email protected]>
Date:   Fri, 29 May 2026 20:32:10 +0900

Tighten debouncing gating

Diffstat:
MCrossmate/Services/AppServices.swift | 12++++++++++++
1 file changed, 12 insertions(+), 0 deletions(-)

diff --git a/Crossmate/Services/AppServices.swift b/Crossmate/Services/AppServices.swift @@ -140,6 +140,11 @@ final class AppServices { private let gameListFreshenCooldown: TimeInterval = 300 private var fresheningPuzzleGridKeys: Set<String> = [] private var lastRemotePuzzleGridFreshenAt: [String: Date] = [:] + /// Collapses bursts of remote-push grid refreshes, but only while the + /// engagement websocket is live for the game (see + /// `shouldSkipRecentRemotePuzzleGridFreshen`). When the live channel is + /// down, the push path is the sole convergence mechanism and is not + /// debounced. private let remotePuzzleGridFreshenDebounce: TimeInterval = 5 private var isGameListVisible = false private var latestLocalSelections: [UUID: PlayerSelection] = [:] @@ -1420,6 +1425,13 @@ final class AppServices { scope: CKDatabase.Scope, label: String ) -> Bool { + // The debounce only suppresses refreshes that the live channel already + // covers. When the engagement websocket is live for this game, grid + // deltas arrive over it and the push-driven fetch is redundant, so + // collapsing a burst of pushes is harmless. When it is not live, the + // CK-push path is the only thing converging the grid — never skip it, + // or convergence stalls exactly when the live overlay is down. + guard engagementStatus.isLive(gameID: gameID) else { return false } let key = puzzleGridFreshenKey(gameID: gameID, scope: scope) guard let last = lastRemotePuzzleGridFreshenAt[key] else { return false } let elapsed = Date().timeIntervalSince(last)