crossmate

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

commit 61c2e4f7a62e8e3be51346495489801edbfa6a3a
parent b016b8717a3561c655cfcf114c9d7fec5b091b23
Author: Michael Camilleri <[email protected]>
Date:   Thu,  2 Jul 2026 00:43:22 +0900

Report swallowed journal and moves save failures

Two Core Data writers dropped a failed save with nothing but a console
print, so the failure never reached the event log the user collects from
Diagnostics. When MovesJournal.persist failed to save an entry, the
replay upload — which reads the persisted rows on a separate context —
silently truncated a finished game's uploaded replay. When
MovesUpdater.persistAndMerge failed, an entire flush of buffered grid
edits was lost the same way; its own comment already names this the
journal-drop bug.

This commit routes both failures through the event log at error level.
PersistenceController already holds an eventLog, so rather than thread a
new dependency through each writer, its property is exposed and read via
the persistence the writers already own. That read is safe off the main
actor because EventLog is Sendable, unlike the container beside it. Each
save runs off the main actor, so the log call hops back through a
main-actor Task, mirroring the peer-ledger path in GameStore. The
rollback and early return are unchanged, and the redundant print is
removed because EventLog already mirrors every entry to the unified
system log.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

Diffstat:
MCrossmate/Persistence/Journal.swift | 6+++++-
MCrossmate/Persistence/PersistenceController.swift | 2+-
MCrossmate/Sync/MovesUpdater.swift | 6+++++-
3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/Crossmate/Persistence/Journal.swift b/Crossmate/Persistence/Journal.swift @@ -403,6 +403,7 @@ final class MovesJournal { private func persist(_ value: JournalValue, gameID: UUID) { let ctx = backgroundContext + let eventLog = persistence.eventLog ctx.perform { let entity = JournalEntity(context: ctx) // Local log: leave the source key nil (this device's own row). @@ -416,7 +417,10 @@ final class MovesJournal { do { try ctx.save() } catch { - print("MovesJournal: failed to persist entry: \(error)") + let message = "MovesJournal: failed to persist entry: \(error)" + Task { @MainActor in + eventLog?.note(message, level: "error") + } ctx.rollback() } } diff --git a/Crossmate/Persistence/PersistenceController.swift b/Crossmate/Persistence/PersistenceController.swift @@ -12,7 +12,7 @@ final class PersistenceController { var viewContext: NSManagedObjectContext { container.viewContext } - private let eventLog: EventLog? + let eventLog: EventLog? init(inMemory: Bool = false, storeURL: URL? = nil, eventLog: EventLog? = nil) { self.eventLog = eventLog diff --git a/Crossmate/Sync/MovesUpdater.swift b/Crossmate/Sync/MovesUpdater.swift @@ -218,7 +218,11 @@ actor MovesUpdater { do { try context.save() } catch { - print("MovesUpdater: failed to save context: \(error)") + let message = "MovesUpdater: failed to save context: \(error)" + let eventLog = persistence.eventLog + Task { @MainActor in + eventLog?.note(message, level: "error") + } return nil } }