commit 452f858ec29608bf522482d1eca19c694b9ca907
parent 4208cbfcd4d46501ba9fbb51cba61f04fc884cd2
Author: Michael Camilleri <[email protected]>
Date: Fri, 8 May 2026 22:09:50 +0900
Fetch current puzzle zone on push
Remote notifications were waking the receiver, but the generic CKSyncEngine
fetch could still leave the open puzzle stale until a later foreground sync or
poll.
This commit targets the push-triggered fetch when a puzzle is already open.
AppServices now derives the current game’s database scope and zone ID, then
asks SyncEngine to fetch that zone immediately using
CKSyncEngine.FetchChangesOptions. If no current puzzle can be targeted, the
existing broad fetch remains the fallback.
Co-Authored-By: Codex GPT 5.5 <[email protected]>
Diffstat:
2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/Crossmate/Services/AppServices.swift b/Crossmate/Services/AppServices.swift
@@ -276,9 +276,29 @@ final class AppServices {
}
guard await ensureICloudSyncStarted() else { return }
syncMonitor.note("remote notification: \(summary)")
- await syncMonitor.run("remote-notification fetch") {
- try await syncEngine.fetchChanges(source: "push")
+ if let target = currentPuzzleFetchTarget() {
+ await syncMonitor.run("remote-notification current-zone fetch") {
+ try await syncEngine.fetchChanges(
+ source: "push-current-zone",
+ targetDatabase: target.database,
+ zoneIDs: [target.zoneID]
+ )
+ }
+ } else {
+ await syncMonitor.run("remote-notification fetch") {
+ try await syncEngine.fetchChanges(source: "push")
+ }
}
+ await refreshSnapshot()
+ }
+
+ private func currentPuzzleFetchTarget() -> (database: CKDatabase.Scope, zoneID: CKRecordZone.ID)? {
+ guard let entity = store.currentEntity,
+ let zoneName = entity.ckZoneName
+ else { return nil }
+ let database: CKDatabase.Scope = entity.databaseScope == 1 ? .shared : .private
+ let ownerName = entity.ckZoneOwnerName ?? CKCurrentUserDefaultName
+ return (database, CKRecordZone.ID(zoneName: zoneName, ownerName: ownerName))
}
private func ensureICloudSyncStarted() async -> Bool {
diff --git a/Crossmate/Sync/SyncEngine.swift b/Crossmate/Sync/SyncEngine.swift
@@ -368,12 +368,31 @@ actor SyncEngine {
// MARK: - Explicit sync triggers (called by AppServices / diagnostics view)
- func fetchChanges(source: String = "manual") async throws {
+ func fetchChanges(
+ source: String = "manual",
+ targetDatabase: CKDatabase.Scope? = nil,
+ zoneIDs: [CKRecordZone.ID] = []
+ ) async throws {
currentFetchSource = source
defer { currentFetchSource = nil }
- async let p: Void = privateEngine?.fetchChanges() ?? ()
- async let s: Void = sharedEngine?.fetchChanges() ?? ()
- _ = try await (p, s)
+ let options = zoneIDs.isEmpty
+ ? CKSyncEngine.FetchChangesOptions()
+ : CKSyncEngine.FetchChangesOptions(scope: .zoneIDs(zoneIDs))
+
+ switch targetDatabase {
+ case .private:
+ try await privateEngine?.fetchChanges(options)
+ case .shared:
+ try await sharedEngine?.fetchChanges(options)
+ case .public:
+ return
+ case .none:
+ async let p: Void = privateEngine?.fetchChanges(options) ?? ()
+ async let s: Void = sharedEngine?.fetchChanges(options) ?? ()
+ _ = try await (p, s)
+ case .some:
+ return
+ }
}
func pushChanges() async throws {