From 304422cf6f42ed6dea11cf7d4650f8fe62869a26 Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Fri, 8 May 2026 13:28:18 -0400 Subject: [PATCH] 05/08 Updated code: fix some issues --- JanitorialQC.xcodeproj/project.pbxproj | 31 ++++++++++++++++++++++---- JanitorialQC/Sync/SyncManager.swift | 17 ++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/JanitorialQC.xcodeproj/project.pbxproj b/JanitorialQC.xcodeproj/project.pbxproj index c61bf61..aac01ca 100644 --- a/JanitorialQC.xcodeproj/project.pbxproj +++ b/JanitorialQC.xcodeproj/project.pbxproj @@ -29,9 +29,22 @@ B37875952FA6358F0088F40B /* JanitorialQCUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JanitorialQCUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + B37875E02FA771440088F40B /* Exceptions for "JanitorialQC" folder in "JanitorialQC" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = B378757B2FA6358E0088F40B /* JanitorialQC */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + /* Begin PBXFileSystemSynchronizedRootGroup section */ B378757E2FA6358E0088F40B /* JanitorialQC */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + B37875E02FA771440088F40B /* Exceptions for "JanitorialQC" folder in "JanitorialQC" target */, + ); path = JanitorialQC; sourceTree = ""; }; @@ -395,17 +408,22 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = SB7DNYC9TY; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = JanitorialQC/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Janitorial QC"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSCameraUsageDescription = "JQC uses the camera to capture photos of inspection findings."; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "JQC saves inspection photos to your photo library."; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "JQC reads photos from your library to attach to inspection reports."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; IPHONEOS_DEPLOYMENT_TARGET = 17; LD_RUNPATH_SEARCH_PATHS = ( @@ -425,7 +443,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 2; }; name = Debug; }; @@ -434,17 +452,22 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = SB7DNYC9TY; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = JanitorialQC/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Janitorial QC"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_NSCameraUsageDescription = "JQC uses the camera to capture photos of inspection findings."; + INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "JQC saves inspection photos to your photo library."; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "JQC reads photos from your library to attach to inspection reports."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; IPHONEOS_DEPLOYMENT_TARGET = 17; LD_RUNPATH_SEARCH_PATHS = ( @@ -464,7 +487,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 2; }; name = Release; }; diff --git a/JanitorialQC/Sync/SyncManager.swift b/JanitorialQC/Sync/SyncManager.swift index a64dbb4..39e74c7 100644 --- a/JanitorialQC/Sync/SyncManager.swift +++ b/JanitorialQC/Sync/SyncManager.swift @@ -169,7 +169,24 @@ class SyncManager: ObservableObject { .filter { $0.syncStatus == "pending" } .sorted { $0.createdAt < $1.createdAt } + // Pre-fetch all LocalInspections once for the parent-failed guard below. + let allInspections = (try? context.fetch(FetchDescriptor())) ?? [] + 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" { + issue.syncStatus = "failed" + issue.syncErrorMessage = "Parent inspection failed to sync — issue cannot be submitted." + try? context.save() + syncError = "Issue \(issue.localId.prefix(8))\u{2026} blocked: parent inspection did not sync." + continue + } + do { let issueId = try await APIClient.shared.submitIssue(issue) issue.serverId = issueId