06/23 Fix High-impact issues
This commit is contained in:
@@ -268,6 +268,16 @@ class SyncManager: ObservableObject {
|
||||
dup.uploadStatus = "uploaded"
|
||||
}
|
||||
|
||||
// Pre-fetch parent records ONCE before the loop.
|
||||
// Without this, every successful photo upload fetched ALL LocalInspection
|
||||
// and ALL LocalIssue records from SwiftData to find the parent —
|
||||
// N photos → 2N full-table fetches. Pre-fetching here reduces that
|
||||
// to 2 fetches regardless of how many photos are in the queue.
|
||||
// Fetch-all + filter in Swift — #Predicate with a captured String variable
|
||||
// causes "LocalInspection is ambiguous" under Xcode 26 (CLAUDE.md rule 3).
|
||||
let allInspections = (try? context.fetch(FetchDescriptor<LocalInspection>())) ?? []
|
||||
let allIssues = (try? context.fetch(FetchDescriptor<LocalIssue>())) ?? []
|
||||
|
||||
for photo in pending {
|
||||
do {
|
||||
let serverPath = try await APIClient.shared.uploadPhoto(
|
||||
@@ -277,20 +287,18 @@ class SyncManager: ObservableObject {
|
||||
photo.serverPath = serverPath
|
||||
photo.uploadStatus = "uploaded"
|
||||
|
||||
// Update parent inspection form field value
|
||||
// Update parent inspection form field value.
|
||||
// Pre-fetched before the loop — not repeated per photo.
|
||||
if photo.entityType == "inspection", let fieldId = photo.fieldId {
|
||||
let entityId = photo.entityLocalId
|
||||
// Fetch-all + filter in Swift — #Predicate with a captured String
|
||||
// variable causes "LocalInspection is ambiguous" under Xcode 26
|
||||
// SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor (CLAUDE.md rule 3).
|
||||
let allInspections = (try? context.fetch(FetchDescriptor<LocalInspection>())) ?? []
|
||||
allInspections.first(where: { $0.localId == entityId })?.setValue(serverPath, forFieldId: fieldId)
|
||||
allInspections.first(where: { $0.localId == entityId })?
|
||||
.setValue(serverPath, forFieldId: fieldId)
|
||||
}
|
||||
|
||||
// Update parent issue photo paths array
|
||||
// Update parent issue photo paths array.
|
||||
// Pre-fetched before the loop — not repeated per photo.
|
||||
if photo.entityType == "issue" {
|
||||
let entityId = photo.entityLocalId
|
||||
let allIssues = (try? context.fetch(FetchDescriptor<LocalIssue>())) ?? []
|
||||
if let issue = allIssues.first(where: { $0.localId == entityId }) {
|
||||
var paths = issue.photoServerPaths
|
||||
if !paths.contains(serverPath) { paths.append(serverPath) }
|
||||
@@ -328,12 +336,12 @@ class SyncManager: ObservableObject {
|
||||
inspection.status = "synced"
|
||||
|
||||
// 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 }) {
|
||||
parent.followUpRequired = false
|
||||
parent.followUpNote = nil
|
||||
}
|
||||
// Reuses the `all` array already fetched at the top of this
|
||||
// function — avoids a redundant full-table fetch per inspection.
|
||||
if let parentLocalId = inspection.parentLocalId,
|
||||
let parent = all.first(where: { $0.localId == parentLocalId }) {
|
||||
parent.followUpRequired = false
|
||||
parent.followUpNote = nil
|
||||
}
|
||||
|
||||
try? context.save()
|
||||
@@ -583,6 +591,10 @@ class SyncManager: ObservableObject {
|
||||
}
|
||||
|
||||
func updatePendingCount(context: ModelContext) {
|
||||
// Fetch-all + filter in Swift — #Predicate with string literals is
|
||||
// banned under Xcode 26 SWIFT_DEFAULT_ACTOR_ISOLATION (CLAUDE.md rule 3).
|
||||
// These fetches are lightweight (no relationships loaded) and run once
|
||||
// per sync cycle at the very end, not in a hot loop.
|
||||
let inspCount = (try? context.fetch(FetchDescriptor<LocalInspection>()))?
|
||||
.filter { $0.syncStatus == "pending" }.count ?? 0
|
||||
let issueCount = (try? context.fetch(FetchDescriptor<LocalIssue>()))?
|
||||
|
||||
Reference in New Issue
Block a user