diff --git a/Andromida.xcodeproj/project.pbxproj b/Andromida.xcodeproj/project.pbxproj index 0daff51..e9c8cc0 100644 --- a/Andromida.xcodeproj/project.pbxproj +++ b/Andromida.xcodeproj/project.pbxproj @@ -573,7 +573,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2; + MARKETING_VERSION = 1.3; PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(PRODUCT_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; @@ -610,7 +610,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.2; + MARKETING_VERSION = 1.3; PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(PRODUCT_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; diff --git a/Andromida/App/State/RitualStore.swift b/Andromida/App/State/RitualStore.swift index b9ad64b..553ebe8 100644 --- a/Andromida/App/State/RitualStore.swift +++ b/Andromida/App/State/RitualStore.swift @@ -4,7 +4,6 @@ import SwiftData import CoreData import Bedrock import WidgetKit -import os @MainActor @Observable @@ -21,12 +20,7 @@ final class RitualStore: RitualStoreProviding { @ObservationIgnored private let isRunningTests: Bool @ObservationIgnored private let dayFormatter: DateFormatter @ObservationIgnored private let displayFormatter: DateFormatter - @ObservationIgnored private var remoteChangeObserver: NSObjectProtocol? - @ObservationIgnored private let syncLogger = Logger( - subsystem: Bundle.main.bundleIdentifier ?? "Andromida", - category: "CloudKitSync" - ) - @ObservationIgnored private var remoteChangeEventCount: Int = 0 + @ObservationIgnored private let cloudKitSyncManager: SwiftDataCloudKitSyncManager private(set) var rituals: [Ritual] = [] private(set) var currentRituals: [Ritual] = [] @@ -81,6 +75,10 @@ final class RitualStore: RitualStoreProviding { self.isRunningTests = isRunningTests self.dayFormatter = DateFormatter() self.displayFormatter = DateFormatter() + self.cloudKitSyncManager = SwiftDataCloudKitSyncManager( + isEnabled: !isRunningTests, + logIdentifier: "AndromidaCloudKitSync" + ) dayFormatter.calendar = calendar dayFormatter.dateFormat = "yyyy-MM-dd" displayFormatter.calendar = calendar @@ -98,37 +96,20 @@ final class RitualStore: RitualStoreProviding { nowProvider() } - deinit { - if let observer = remoteChangeObserver { - NotificationCenter.default.removeObserver(observer) - } - } - /// Observes CloudKit remote change notifications to auto-refresh UI when iCloud data syncs. private func observeRemoteChanges() { - syncLogger.info("Starting CloudKit remote change observation") - remoteChangeObserver = NotificationCenter.default.addObserver( - forName: .NSPersistentStoreRemoteChange, - object: nil, - queue: .main - ) { [weak self] _ in - // Hop to the main actor and capture a strong reference safely there - Task { @MainActor [weak self] in - guard let strongSelf = self else { return } - strongSelf.handleRemoteStoreChange() - } + cloudKitSyncManager.startObserving { [weak self] in + self?.handleRemoteStoreChange() } } private func handleRemoteStoreChange() { - remoteChangeEventCount += 1 - lastRemoteChangeDate = now() + let eventCount = cloudKitSyncManager.remoteChangeEventCount + lastRemoteChangeDate = cloudKitSyncManager.lastRemoteChangeDate // SwiftData may keep stale registered objects in long-lived contexts. // Recreate the context on remote store changes so fetches observe latest merged values. modelContext = ModelContext(modelContainer) - syncLogger.info( - "Received remote store change #\(self.remoteChangeEventCount, privacy: .public); reloading rituals" - ) + Design.debugLog("Received remote store change #\(eventCount); reloading rituals") reloadRituals() // Also refresh widgets when data arrives from other devices WidgetCenter.shared.reloadAllTimelines()