Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
bd09c14a02
commit
db06b43364
@ -83,21 +83,10 @@ final class AppDataStore: SwiftDataCloudKitStore {
|
||||
init(modelContext: ModelContext) {
|
||||
self.modelContainer = modelContext.container
|
||||
self.modelContext = modelContext
|
||||
startSyncObservation()
|
||||
startObservingCloudKitRemoteChanges()
|
||||
reloadData()
|
||||
}
|
||||
|
||||
private func startSyncObservation() {
|
||||
cloudKitSyncManager.startObserving { [weak self] in
|
||||
guard let self else { return }
|
||||
_ = self.cloudKitSyncManager.processObservedRemoteChange(
|
||||
modelContext: &self.modelContext,
|
||||
modelContainer: self.modelContainer
|
||||
)
|
||||
self.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
func reloadData() {
|
||||
// Fetch your app entities and recompute derived state.
|
||||
refreshVersion &+= 1
|
||||
@ -105,6 +94,29 @@ final class AppDataStore: SwiftDataCloudKitStore {
|
||||
}
|
||||
```
|
||||
|
||||
If your store needs post-remote side effects (for example widget timeline reload), use:
|
||||
|
||||
```swift
|
||||
startObservingCloudKitRemoteChanges {
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
```
|
||||
|
||||
If your store needs save side effects/error handling, prefer Bedrock protocol hooks:
|
||||
|
||||
```swift
|
||||
func didSaveAndReloadData() {
|
||||
// optional side effects (widgets, cache invalidation, etc.)
|
||||
}
|
||||
|
||||
func handleSaveAndReloadError(_ error: Error) {
|
||||
// set store error state
|
||||
}
|
||||
|
||||
// Then call the shared default:
|
||||
saveAndReload()
|
||||
```
|
||||
|
||||
## 4) Integrate `SwiftDataRefreshThrottler`
|
||||
|
||||
Use throttling for on-appear and tab/scene refresh paths:
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SwiftDataStore.swift
|
||||
// Bedrock
|
||||
//
|
||||
// Generic macOS fallback pulse wiring for SwiftData + CloudKit stores.
|
||||
// Shared store protocols/defaults for SwiftData, including CloudKit helpers.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -15,6 +15,47 @@ public protocol SwiftDataStore: AnyObject {
|
||||
|
||||
/// App-specific fetch/recompute entry point.
|
||||
func reloadData()
|
||||
|
||||
/// App-specific side effects after a successful save+reload cycle.
|
||||
func didSaveAndReloadData()
|
||||
|
||||
/// App-specific error handling when save+reload fails.
|
||||
func handleSaveAndReloadError(_ error: Error)
|
||||
}
|
||||
|
||||
public extension SwiftDataStore {
|
||||
/// Default no-op success hook.
|
||||
func didSaveAndReloadData() {}
|
||||
|
||||
/// Default error handling hook.
|
||||
func handleSaveAndReloadError(_ error: Error) {
|
||||
Design.debugLog(
|
||||
"\(String(describing: type(of: self))): failed saveAndReload error=\(error.localizedDescription)"
|
||||
)
|
||||
}
|
||||
|
||||
/// Saves the context, reloads data, then runs optional hooks.
|
||||
///
|
||||
/// If hooks are omitted, protocol defaults are used:
|
||||
/// - success: `didSaveAndReloadData()`
|
||||
/// - failure: `handleSaveAndReloadError(_:)`
|
||||
func saveAndReload(
|
||||
onSuccess: (() -> Void)? = nil,
|
||||
onFailure: ((Error) -> Void)? = nil
|
||||
) {
|
||||
do {
|
||||
try modelContext.save()
|
||||
reloadData()
|
||||
(onSuccess ?? didSaveAndReloadData)()
|
||||
} catch {
|
||||
(onFailure ?? handleSaveAndReloadError)(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience overload that always uses protocol hooks.
|
||||
func saveAndReload() {
|
||||
saveAndReload(onSuccess: didSaveAndReloadData, onFailure: handleSaveAndReloadError)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@ -38,6 +79,33 @@ public extension SwiftDataCloudKitStore {
|
||||
cloudKitSyncManager.hasReceivedRemoteChange(since: date)
|
||||
}
|
||||
|
||||
/// Starts observing remote CloudKit-backed store changes.
|
||||
///
|
||||
/// - Parameter afterReload: Optional callback that runs after the default `reloadData()` handling.
|
||||
func startObservingCloudKitRemoteChanges(afterReload: (@MainActor () -> Void)? = nil) {
|
||||
cloudKitSyncManager.startObserving { [weak self] in
|
||||
self?.handleObservedCloudKitRemoteChange(afterReload: afterReload)
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies the default handling for an observed remote CloudKit merge.
|
||||
///
|
||||
/// - Parameter afterReload: Optional callback that runs after `reloadData()`.
|
||||
func handleObservedCloudKitRemoteChange(afterReload: (@MainActor () -> Void)? = nil) {
|
||||
let result = cloudKitSyncManager.processObservedRemoteChange(
|
||||
modelContext: &modelContext,
|
||||
modelContainer: modelContainer
|
||||
)
|
||||
|
||||
Design.debugLog(
|
||||
"\(String(describing: type(of: self))): received remote store change #\(result.eventCount); " +
|
||||
"rebuiltContext=\(result.didRebuildModelContext)"
|
||||
)
|
||||
|
||||
reloadData()
|
||||
afterReload?()
|
||||
}
|
||||
|
||||
/// Production fallback: pulses CloudKit import scheduling while app is active on Mac runtime.
|
||||
func forceCloudKitImportPulse(reason: String) {
|
||||
guard cloudKitSyncManager.triggerMacImportPulse(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user