From 1b5dc684aa9914c6cd257735af391643ac3f7837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Tue, 31 Mar 2026 09:33:58 +0700 Subject: [PATCH] fix: connection list mismatch after iCloud sync (#516) ConnectionStorage.saveConnections() set cache BEFORE writing to UserDefaults. If encoding failed or a race condition occurred, cache diverged from disk. All consumers (welcome window, dock menu, connection switcher) read stale data. Fix: clear cache AFTER successful write (matching GroupStorage/TagStorage pattern) and invalidate cache before applying remote sync changes. --- TablePro/Core/Storage/ConnectionStorage.swift | 8 ++++++-- TablePro/Core/Sync/SyncCoordinator.swift | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/TablePro/Core/Storage/ConnectionStorage.swift b/TablePro/Core/Storage/ConnectionStorage.swift index 546689d91..a2c465b18 100644 --- a/TablePro/Core/Storage/ConnectionStorage.swift +++ b/TablePro/Core/Storage/ConnectionStorage.swift @@ -50,18 +50,22 @@ final class ConnectionStorage { /// Save all connections func saveConnections(_ connections: [DatabaseConnection]) { - cachedConnections = connections - let storedConnections = connections.map { StoredConnection(from: $0) } do { let data = try encoder.encode(storedConnections) defaults.set(data, forKey: connectionsKey) + cachedConnections = nil } catch { Self.logger.error("Failed to save connections: \(error)") } } + /// Invalidate the in-memory cache so the next load reads fresh from UserDefaults. + func invalidateCache() { + cachedConnections = nil + } + /// Add a new connection func addConnection(_ connection: DatabaseConnection, password: String? = nil) { var connections = loadConnections() diff --git a/TablePro/Core/Sync/SyncCoordinator.swift b/TablePro/Core/Sync/SyncCoordinator.swift index fef79093c..c6fea0b6d 100644 --- a/TablePro/Core/Sync/SyncCoordinator.swift +++ b/TablePro/Core/Sync/SyncCoordinator.swift @@ -379,6 +379,9 @@ final class SyncCoordinator { private func applyRemoteChanges(_ result: PullResult) { let settings = AppSettingsStorage.shared.loadSync() + // Invalidate caches before applying remote data to ensure fresh reads + ConnectionStorage.shared.invalidateCache() + // Suppress change tracking during remote apply to avoid sync loops changeTracker.isSuppressed = true defer {