commit ead9dbcfe6399be76913e3da4b48a73ee3fa094d
parent 989419b0d641c27fc1194b68b70feac6f74b07ce
Author: Michael Camilleri <[email protected]>
Date: Wed, 15 Apr 2026 02:19:40 +0900
Ensure game creation triggers iCloud sync
Diffstat:
2 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/Crossmate/CrossmateApp.swift b/Crossmate/CrossmateApp.swift
@@ -104,6 +104,15 @@ struct RootView: View {
nytAuth.loadStoredSession()
ubiquityMonitor.start()
+ // Wire local outbox changes → sync engine push
+ store.onPendingChangesAvailable = { [syncEngine, syncMonitor] in
+ Task {
+ await Self.run("local pending push", monitor: syncMonitor) {
+ try await syncEngine.pushChanges()
+ }
+ }
+ }
+
// Wire app delegate → sync engine fetch
appDelegate.onRemoteNotification = { [syncEngine, syncMonitor] in
await Self.run("remote-notification fetch", monitor: syncMonitor) {
@@ -222,13 +231,6 @@ private struct GameDestinationView: View {
do {
let (game, mutator) = try store.loadGame(id: gameID)
let playerSession = PlayerSession(game: game, mutator: mutator)
- mutator.onLocalMutation = { [syncEngine, syncMonitor] in
- Task {
- await RootView.run("local mutation push", monitor: syncMonitor) {
- try await syncEngine.pushChanges()
- }
- }
- }
self._session = State(initialValue: playerSession)
} catch {
self._loadError = State(initialValue: String(describing: error))
diff --git a/Crossmate/Persistence/GameStore.swift b/Crossmate/Persistence/GameStore.swift
@@ -78,6 +78,12 @@ final class GameStore {
private(set) var currentMutator: GameMutator?
private(set) var currentEntity: GameEntity?
+ /// Called after local work has been enqueued in the pending-change outbox.
+ /// The app layer wires this to the sync engine so persistence stays
+ /// independent of CloudKit scheduling.
+ @ObservationIgnored
+ var onPendingChangesAvailable: (() -> Void)?
+
init(persistence: PersistenceController) {
self.persistence = persistence
repairSyncedGamesIfNeeded()
@@ -109,7 +115,7 @@ final class GameStore {
let game = Game(puzzle: puzzle)
restore(game: game, from: entity)
- let mutator = GameMutator(game: game, gameEntity: entity, context: context)
+ let mutator = makeMutator(game: game, entity: entity)
currentGame = game
currentMutator = mutator
@@ -162,6 +168,7 @@ final class GameStore {
RecordSerializer.enqueueGamePending(for: entity, in: context)
try context.save()
+ notifyPendingChangesAvailable()
return gameID
}
@@ -227,7 +234,7 @@ final class GameStore {
let game = Game(puzzle: puzzle)
restore(game: game, from: entity)
- let mutator = GameMutator(game: game, gameEntity: entity, context: context)
+ let mutator = makeMutator(game: game, entity: entity)
currentGame = game
currentMutator = mutator
@@ -279,6 +286,7 @@ final class GameStore {
RecordSerializer.enqueueGamePending(for: entity, in: context)
try context.save()
+ notifyPendingChangesAvailable()
return (entity, puzzle)
}
@@ -345,6 +353,18 @@ final class GameStore {
}
}
+ private func makeMutator(game: Game, entity: GameEntity) -> GameMutator {
+ let mutator = GameMutator(game: game, gameEntity: entity, context: context)
+ mutator.onLocalMutation = { [weak self] in
+ self?.notifyPendingChangesAvailable()
+ }
+ return mutator
+ }
+
+ private func notifyPendingChangesAvailable() {
+ onPendingChangesAvailable?()
+ }
+
// MARK: - Remote changes
/// Routes incoming remote cell changes through the current `GameMutator`,