crossmate

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

commit 64a417dffdc59be84f3cb50e00ce7adc326b88e6
parent 0b3b5073a635a57d9e456304c9b5fa4910f89375
Author: Michael Camilleri <[email protected]>
Date:   Mon, 13 Apr 2026 02:09:49 +0900

Add more iCloud debugging support

Diffstat:
MCrossmate/Sync/SyncEngine.swift | 16++++++++++++++++
MCrossmate/Views/SyncDiagnosticsView.swift | 12++++++++++++
2 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/Crossmate/Sync/SyncEngine.swift b/Crossmate/Sync/SyncEngine.swift @@ -531,6 +531,22 @@ actor SyncEngine { let hasZoneToken: Bool } + /// Clears the local sync bookkeeping (zone/subscription created flags and + /// change tokens) so the next bootstrap actually hits the network. Used by + /// the diagnostics screen to recover from stale flags. Does not touch the + /// outbox or any game data. + func resetSyncState() async { + let context = persistence.container.newBackgroundContext() + context.performAndWait { + let state = SyncStateEntity.current(in: context) + state.zoneCreated = false + state.subscriptionCreated = false + state.privateDatabaseToken = nil + state.privateZoneToken = nil + try? context.save() + } + } + func diagnosticSnapshot() async -> DiagnosticSnapshot { let accountStatus: CKAccountStatus do { diff --git a/Crossmate/Views/SyncDiagnosticsView.swift b/Crossmate/Views/SyncDiagnosticsView.swift @@ -49,6 +49,11 @@ struct SyncDiagnosticsView: View { } } .disabled(isSyncing) + + Button("Reset Sync State", role: .destructive) { + Task { await resetSyncState() } + } + .disabled(isSyncing) } Section("Recent Events") { @@ -90,6 +95,13 @@ struct SyncDiagnosticsView: View { // MARK: - Actions + private func resetSyncState() async { + await syncEngine.resetSyncState() + syncMonitor.note("Sync state reset (zone/subscription flags and tokens cleared)") + let snapshot = await syncEngine.diagnosticSnapshot() + syncMonitor.updateSnapshot(snapshot) + } + private func runFullSync() async { guard !isSyncing else { return } isSyncing = true