05/08 Updated code: fix some issues 3

This commit is contained in:
Nguyen Ngo
2026-05-08 16:44:02 -04:00
parent 346eb270ee
commit 606eda9521
9 changed files with 200 additions and 353 deletions
+11 -17
View File
@@ -48,7 +48,9 @@ class SyncManager: ObservableObject {
// Full Sync
func triggerSync() async {
guard isOnline, let context = modelContext else { return }
// Do not sync unless authenticated avoids 401 loops before
// restoreSession() completes on first launch.
guard isOnline, let context = modelContext, AuthManager.shared.isAuthenticated else { return }
isSyncing = true
syncError = nil
defer { isSyncing = false }
@@ -116,8 +118,6 @@ class SyncManager: ObservableObject {
// Outbox: Inspections
private func processInspectionQueue(context: ModelContext) async {
// Fetch all and filter in Swift to avoid #Predicate compound
// string comparison issues across Xcode versions.
guard let all = try? context.fetch(FetchDescriptor<LocalInspection>()) else { return }
let pending = all
.filter { $0.status == "completed" && $0.syncStatus == "pending" }
@@ -135,10 +135,7 @@ class SyncManager: ObservableObject {
inspection.syncStatus = "synced"
inspection.status = "synced"
// Clear follow-up flag on parent
// Use parentLocalId (always set at creation) rather than
// parentServerId (nil until parent syncs) so the badge clears
// regardless of whether the parent has been synced yet.
// Clear follow-up flag on parent.
if let parentLocalId = inspection.parentLocalId {
let allInspections = try? context.fetch(FetchDescriptor<LocalInspection>())
if let parent = allInspections?.first(where: { $0.localId == parentLocalId }) {
@@ -169,17 +166,16 @@ class SyncManager: ObservableObject {
.filter { $0.syncStatus == "pending" }
.sorted { $0.createdAt < $1.createdAt }
// Pre-fetch all LocalInspections once for the parent-failed guard below.
// Pre-fetch all inspections to check parent sync status.
let allInspections = (try? context.fetch(FetchDescriptor<LocalInspection>())) ?? []
for issue in pending {
// Guard: if the parent inspection permanently failed to sync, submitting
// this issue without an inspection_id would create an orphaned server
// record. Skip the issue and mark it failed so the inspector can see
// the error rather than losing the association silently.
if let parentInspection = allInspections.first(where: {
$0.localId == issue.inspectionLocalId
}), parentInspection.syncStatus == "failed" {
// Guard: if the parent inspection permanently failed to sync,
// submitting this issue without an inspection_id would create an
// orphaned server record. Mark it failed immediately instead.
let parentId = issue.inspectionLocalId
let parent = allInspections.first(where: { $0.localId == parentId })
if parent?.syncStatus == "failed" {
issue.syncStatus = "failed"
issue.syncErrorMessage = "Parent inspection failed to sync — issue cannot be submitted."
try? context.save()
@@ -210,8 +206,6 @@ class SyncManager: ObservableObject {
guard isOnline, let context = modelContext else { return }
do {
// Sequential fetches avoid Swift 6 actor-isolation warnings
// on Decodable structs used across async boundaries.
let facilitiesData: FacilitiesResponseData =
try await APIClient.shared.request("/api/v1/facilities")
let templatesData: TemplatesResponseData =