commit c4534b5531f266f8db128efdde3006301629c0fd
parent 389d8cd6a538abda8495edf14b89d8910cbbaaff
Author: Michael Camilleri <[email protected]>
Date: Fri, 17 Apr 2026 14:52:19 +0900
Inject sync services via environment
Prior to this commit, GameListView and SettingsView held syncEngine and
syncMonitor only to forward them down to SyncDiagnosticsView, which was
the sole consumer. This commit removes the pass-through properties and
inject both at the app root instead: SyncMonitor as an @Observable
environment value, SyncEngine via an @Entry EnvironmentValues key
(matching the existing nytPuzzleFetcher pattern, since actors can't be
@Observable).
Co-Authored-By: Claude Opus 4.7 <[email protected]>
Diffstat:
5 files changed, 15 insertions(+), 12 deletions(-)
diff --git a/Crossmate/CrossmateApp.swift b/Crossmate/CrossmateApp.swift
@@ -19,7 +19,9 @@ struct CrossmateApp: App {
.environment(\.managedObjectContext, services.persistence.viewContext)
.environment(services.nytAuth)
.environment(services.ubiquityMonitor)
+ .environment(services.syncMonitor)
.environment(\.nytPuzzleFetcher, services.nytFetcher)
+ .environment(\.syncEngine, services.syncEngine)
}
}
}
@@ -60,8 +62,6 @@ struct RootView: View {
NavigationStack(path: $navigationPath) {
GameListView(
store: services.store,
- syncEngine: services.syncEngine,
- syncMonitor: services.syncMonitor,
navigationPath: $navigationPath
)
.navigationDestination(for: UUID.self) { gameID in
diff --git a/Crossmate/Sync/SyncEngine.swift b/Crossmate/Sync/SyncEngine.swift
@@ -1,6 +1,11 @@
import CloudKit
import CoreData
import Foundation
+import SwiftUI
+
+extension EnvironmentValues {
+ @Entry var syncEngine: SyncEngine? = nil
+}
/// Owns the CloudKit container, custom zone, and sync lifecycle. All CloudKit
/// operations run on this actor's serial executor, keeping token reads and
diff --git a/Crossmate/Views/GameListView.swift b/Crossmate/Views/GameListView.swift
@@ -3,8 +3,6 @@ import SwiftUI
struct GameListView: View {
let store: GameStore
- let syncEngine: SyncEngine
- let syncMonitor: SyncMonitor
@Binding var navigationPath: NavigationPath
@Environment(\.managedObjectContext) private var viewContext
@@ -112,7 +110,7 @@ struct GameListView: View {
}
}
.sheet(isPresented: $showingSettings) {
- SettingsView(syncEngine: syncEngine, syncMonitor: syncMonitor)
+ SettingsView()
}
.sheet(isPresented: $showingNewGame) {
NewGameSheet(store: store)
diff --git a/Crossmate/Views/SettingsView.swift b/Crossmate/Views/SettingsView.swift
@@ -1,9 +1,6 @@
import SwiftUI
struct SettingsView: View {
- let syncEngine: SyncEngine
- let syncMonitor: SyncMonitor
-
@Environment(NYTAuthService.self) private var nytAuth
@Environment(\.dismiss) private var dismiss
@@ -24,7 +21,7 @@ struct SettingsView: View {
Section("iCloud Sync") {
NavigationLink("Diagnostics") {
- SyncDiagnosticsView(syncEngine: syncEngine, syncMonitor: syncMonitor)
+ SyncDiagnosticsView()
}
}
}
diff --git a/Crossmate/Views/SyncDiagnosticsView.swift b/Crossmate/Views/SyncDiagnosticsView.swift
@@ -2,8 +2,8 @@ import CloudKit
import SwiftUI
struct SyncDiagnosticsView: View {
- let syncEngine: SyncEngine
- let syncMonitor: SyncMonitor
+ @Environment(\.syncEngine) private var syncEngine
+ @Environment(SyncMonitor.self) private var syncMonitor
@State private var isSyncing = false
@@ -93,6 +93,7 @@ struct SyncDiagnosticsView: View {
}
}
.task {
+ guard let syncEngine else { return }
let snapshot = await syncEngine.diagnosticSnapshot()
syncMonitor.updateSnapshot(snapshot)
}
@@ -101,6 +102,7 @@ struct SyncDiagnosticsView: View {
// MARK: - Actions
private func resetSyncState() async {
+ guard let syncEngine else { return }
await syncEngine.resetSyncState()
syncMonitor.note("Sync state reset (zone/subscription flags and tokens cleared)")
let snapshot = await syncEngine.diagnosticSnapshot()
@@ -108,6 +110,7 @@ struct SyncDiagnosticsView: View {
}
private func probeContainer() async {
+ guard let syncEngine else { return }
syncMonitor.note("Starting container probe")
let results = await syncEngine.probeContainer()
for (name, result) in results {
@@ -117,7 +120,7 @@ struct SyncDiagnosticsView: View {
}
private func runFullSync() async {
- guard !isSyncing else { return }
+ guard !isSyncing, let syncEngine else { return }
isSyncing = true
defer { isSyncing = false }