Browse Source

Error handling

polscm32 1 year ago
parent
commit
1e7b721896
49 changed files with 556 additions and 338 deletions
  1. 4 4
      Trio/Sources/APS/APSManager.swift
  2. 12 8
      Trio/Sources/APS/DeviceDataManager.swift
  3. 5 3
      Trio/Sources/APS/FetchGlucoseManager.swift
  4. 39 35
      Trio/Sources/APS/FetchTreatmentsManager.swift
  5. 8 7
      Trio/Sources/APS/OpenAPS/OpenAPS.swift
  6. 14 12
      Trio/Sources/APS/Storage/CarbsStorage.swift
  7. 4 2
      Trio/Sources/APS/Storage/ContactImageStorage.swift
  8. 4 2
      Trio/Sources/APS/Storage/DeterminationStorage.swift
  9. 24 12
      Trio/Sources/APS/Storage/GlucoseStorage.swift
  10. 28 14
      Trio/Sources/APS/Storage/OverrideStorage.swift
  11. 15 8
      Trio/Sources/APS/Storage/PumpHistoryStorage.swift
  12. 58 48
      Trio/Sources/APS/Storage/TempTargetsStorage.swift
  13. 9 2
      Trio/Sources/Application/AppDelegate.swift
  14. 24 20
      Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+TempTargets.swift
  15. 11 7
      Trio/Sources/Modules/Adjustments/AdjustmentsStateModel.swift
  16. 22 20
      Trio/Sources/Modules/Adjustments/View/Overrides/EditOverrideForm.swift
  17. 16 8
      Trio/Sources/Modules/Adjustments/View/TempTargets/AddTempTargetForm.swift
  18. 9 2
      Trio/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift
  19. 6 2
      Trio/Sources/Modules/BasalProfileEditor/BasalProfileEditorStateModel.swift
  20. 2 2
      Trio/Sources/Modules/Calibrations/CalibrationsStateModel.swift
  21. 6 2
      Trio/Sources/Modules/CarbRatioEditor/CarbRatioEditorStateModel.swift
  22. 28 22
      Trio/Sources/Modules/DataTable/DataTableStateModel.swift
  23. 4 2
      Trio/Sources/Modules/Home/HomeStateModel+Setup/BatterySetup.swift
  24. 8 4
      Trio/Sources/Modules/Home/HomeStateModel+Setup/CarbSetup.swift
  25. 2 2
      Trio/Sources/Modules/Home/HomeStateModel+Setup/DeterminationSetup.swift
  26. 4 2
      Trio/Sources/Modules/Home/HomeStateModel+Setup/GlucoseSetup.swift
  27. 8 4
      Trio/Sources/Modules/Home/HomeStateModel+Setup/OverrideSetup.swift
  28. 6 4
      Trio/Sources/Modules/Home/HomeStateModel+Setup/PumpHistorySetup.swift
  29. 8 4
      Trio/Sources/Modules/Home/HomeStateModel+Setup/TempTargetSetup.swift
  30. 9 2
      Trio/Sources/Modules/ISFEditor/ISFEditorStateModel.swift
  31. 17 3
      Trio/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift
  32. 4 2
      Trio/Sources/Modules/Stat/StatStateModel.swift
  33. 9 2
      Trio/Sources/Modules/TargetsEditor/TargetsEditorStateModel.swift
  34. 4 2
      Trio/Sources/Modules/Treatments/TreatmentsStateModel.swift
  35. 4 2
      Trio/Sources/Services/BolusCalculator/BolusCalculationManager.swift
  36. 8 4
      Trio/Sources/Services/Calendar/CalendarManager.swift
  37. 6 4
      Trio/Sources/Services/ContactImage/ContactImageManager.swift
  38. 4 2
      Trio/Sources/Services/HealthKit/HealthKitManager.swift
  39. 6 6
      Trio/Sources/Services/LiveActivity/Data/DataManager.swift
  40. 23 8
      Trio/Sources/Services/LiveActivity/LiveActivityBridge.swift
  41. 16 10
      Trio/Sources/Services/Network/Nightscout/NightscoutManager.swift
  42. 4 2
      Trio/Sources/Services/Network/TidepoolManager.swift
  43. 2 2
      Trio/Sources/Services/RemoteControl/TrioRemoteControl+Bolus.swift
  44. 2 2
      Trio/Sources/Services/RemoteControl/TrioRemoteControl+TempTarget.swift
  45. 1 1
      Trio/Sources/Services/RemoteControl/TrioRemoteControl.swift
  46. 4 2
      Trio/Sources/Services/UserNotifications/UserNotificationsManager.swift
  47. 8 4
      Trio/Sources/Services/WatchManager/AppleWatchManager.swift
  48. 34 11
      Trio/Sources/Services/WatchManager/GarminManager.swift
  49. 3 3
      Trio/Sources/Shortcuts/TempPresets/TempPresetsIntentRequest.swift

+ 4 - 4
Trio/Sources/APS/APSManager.swift

@@ -577,13 +577,13 @@ final class BaseAPSManager: APSManager, Injectable {
             fetchLimit: 1
         )
 
-        let fetchedTempBasal = await privateContext.perform {
+        let fetchedTempBasal = try await privateContext.perform {
             guard let fetchedResults = results as? [PumpEventStored],
                   let tempBasalEvent = fetchedResults.first,
                   let tempBasal = tempBasalEvent.tempBasal,
                   let eventTimestamp = tempBasalEvent.timestamp
             else {
-                return TempBasal(duration: 0, rate: 0, temp: .absolute, timestamp: date)
+                throw APSError.apsError(message: "Failed to fetch temp basal")
             }
 
             let delta = Int((date.timeIntervalSince1970 - eventTimestamp.timeIntervalSince1970) / 60)
@@ -865,9 +865,9 @@ final class BaseAPSManager: APSManager, Injectable {
             batchSize: batchSize
         )
 
-        return await privateContext.perform {
+        return try await privateContext.perform {
             guard let glucoseResults = results as? [GlucoseStored] else {
-                return []
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return glucoseResults

+ 12 - 8
Trio/Sources/APS/DeviceDataManager.swift

@@ -513,15 +513,19 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
         dispatchPrecondition(condition: .onQueue(processQueue))
 
         Task {
-            // filter buggy TBRs > maxBasal from MDT
-            let events = events.filter {
-                // type is optional...
-                guard let type = $0.type, type == .tempBasal else { return true }
-                return $0.dose?.unitsPerHour ?? 0 <= Double(settingsManager.pumpSettings.maxBasal)
+            do {
+                // filter buggy TBRs > maxBasal from MDT
+                let events = events.filter {
+                    // type is optional...
+                    guard let type = $0.type, type == .tempBasal else { return true }
+                    return $0.dose?.unitsPerHour ?? 0 <= Double(settingsManager.pumpSettings.maxBasal)
+                }
+                try await pumpHistoryStorage.storePumpEvents(events)
+                lastEventDate = events.last?.date
+                completion(nil)
+            } catch {
+                debug(.deviceManager, "\(DebuggingIdentifiers.failed) Failed to store pump events: \(error)")
             }
-            try await pumpHistoryStorage.storePumpEvents(events)
-            lastEventDate = events.last?.date
-            completion(nil)
         }
     }
 

+ 5 - 3
Trio/Sources/APS/FetchGlucoseManager.swift

@@ -217,10 +217,12 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
 
     private func processGlucose() async throws -> [BloodGlucose] {
         let results = try await fetchGlucose()
-        guard let results else { return [] }
 
-        return await context.perform {
-            results.map { result in
+        return try await context.perform {
+            guard let results else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
+            return results.map { result in
                 BloodGlucose(
                     sgv: Int(result.glucose),
                     direction: BloodGlucose.Direction(from: result.direction ?? ""),

+ 39 - 35
Trio/Sources/APS/FetchTreatmentsManager.swift

@@ -30,48 +30,52 @@ final class BaseFetchTreatmentsManager: FetchTreatmentsManager, Injectable {
                 debug(.nightscout, "Start fetching carbs and temptargets")
 
                 Task {
-                    // Fetch carbs and temp targets concurrently
-                    async let carbs = self.nightscoutManager.fetchCarbs()
-                    async let tempTargets = self.nightscoutManager.fetchTempTargets()
+                    do {
+                        // Fetch carbs and temp targets concurrently
+                        async let carbs = self.nightscoutManager.fetchCarbs()
+                        async let tempTargets = self.nightscoutManager.fetchTempTargets()
 
-                    // Filter and store if not from "Trio"
-                    let filteredCarbs = await carbs.filter { $0.enteredBy != CarbsEntry.local }
-                    if filteredCarbs.isNotEmpty {
-                        try await self.carbsStorage.storeCarbs(filteredCarbs, areFetchedFromRemote: true)
-                    }
+                        // Filter and store if not from "Trio"
+                        let filteredCarbs = await carbs.filter { $0.enteredBy != CarbsEntry.local }
+                        if filteredCarbs.isNotEmpty {
+                            try await self.carbsStorage.storeCarbs(filteredCarbs, areFetchedFromRemote: true)
+                        }
 
-                    // Filter and store if not from Trio
-                    let filteredTargets = await tempTargets.filter { $0.enteredBy != TempTarget.local }
-                    if filteredTargets.isNotEmpty {
-                        // Sort temp targets by creation date
-                        let sortedTargets = filteredTargets.sorted { $0.createdAt < $1.createdAt }
+                        // Filter and store if not from Trio
+                        let filteredTargets = await tempTargets.filter { $0.enteredBy != TempTarget.local }
+                        if filteredTargets.isNotEmpty {
+                            // Sort temp targets by creation date
+                            let sortedTargets = filteredTargets.sorted { $0.createdAt < $1.createdAt }
 
-                        // Iterate and store each temp target
-                        for (index, tempTarget) in sortedTargets.enumerated() {
-                            // Skip saving if a Temp Target with the same date already exists or it's a cancel target
-                            guard await !self.tempTargetsStorage.existsTempTarget(with: tempTarget.createdAt),
-                                  tempTarget.reason != TempTarget.cancel
-                            else {
-                                debug(
-                                    .nightscout,
-                                    "Skipping temp target with date: \(tempTarget.date ?? Date.distantPast)"
-                                )
-                                continue
-                            }
+                            // Iterate and store each temp target
+                            for (index, tempTarget) in sortedTargets.enumerated() {
+                                // Skip saving if a Temp Target with the same date already exists or it's a cancel target
+                                guard await !self.tempTargetsStorage.existsTempTarget(with: tempTarget.createdAt),
+                                      tempTarget.reason != TempTarget.cancel
+                                else {
+                                    debug(
+                                        .nightscout,
+                                        "Skipping temp target with date: \(tempTarget.date ?? Date.distantPast)"
+                                    )
+                                    continue
+                                }
 
-                            // Create a mutable copy and set enabled for the last temp target
-                            var mutableTempTarget = tempTarget
-                            mutableTempTarget.enabled = (index == sortedTargets.count - 1)
+                                // Create a mutable copy and set enabled for the last temp target
+                                var mutableTempTarget = tempTarget
+                                mutableTempTarget.enabled = (index == sortedTargets.count - 1)
 
-                            // Save to Core Data
-                            await self.tempTargetsStorage.storeTempTarget(tempTarget: mutableTempTarget)
-                        }
+                                // Save to Core Data
+                                try await self.tempTargetsStorage.storeTempTarget(tempTarget: mutableTempTarget)
+                            }
 
-                        // Save the temp targets to JSON so that they get used by oref
-                        self.tempTargetsStorage.saveTempTargetsToStorage(sortedTargets)
+                            // Save the temp targets to JSON so that they get used by oref
+                            self.tempTargetsStorage.saveTempTargetsToStorage(sortedTargets)
 
-                        // Update Adjustments View
-                        Foundation.NotificationCenter.default.post(name: .didUpdateTempTargetConfiguration, object: nil)
+                            // Update Adjustments View
+                            Foundation.NotificationCenter.default.post(name: .didUpdateTempTargetConfiguration, object: nil)
+                        }
+                    } catch {
+                        debug(.default, "\(DebuggingIdentifiers.failed) error in \(#file) \(#function): \(error)")
                     }
                 }
             }

+ 8 - 7
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -111,9 +111,9 @@ final class OpenAPS {
             batchSize: 24
         )
 
-        return await context.perform {
+        return try await context.perform {
             guard let glucoseResults = results as? [GlucoseStored] else {
-                return ""
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             // convert to JSON
@@ -130,9 +130,9 @@ final class OpenAPS {
             ascending: false
         )
 
-        let json = await context.perform {
+        let json = try await context.perform {
             guard let carbResults = results as? [CarbEntryStored] else {
-                return ""
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             var jsonArray = self.jsonConverter.convertToJSON(carbResults)
@@ -180,9 +180,9 @@ final class OpenAPS {
             batchSize: 50
         )
 
-        return await context.perform {
+        return try await context.perform {
             guard let pumpEventResults = results as? [PumpEventStored] else {
-                return nil
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return pumpEventResults.map(\.objectID)
@@ -555,11 +555,12 @@ final class OpenAPS {
                 .apsManager,
                 "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to create pump profile and normal profile: \(error)"
             )
+            throw error
         }
     }
 
     private func iob(pumphistory: JSON, profile: JSON, clock: JSON, autosens: JSON) async throws -> RawJSON {
-        await withCheckedContinuation { continuation in
+        try await withCheckedThrowingContinuation { continuation in
             jsWorker.inCommonContext { worker in
                 worker.evaluateBatch(scripts: [
                     Script(name: Prepare.log),

+ 14 - 12
Trio/Sources/APS/Storage/CarbsStorage.swift

@@ -348,9 +348,9 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             ascending: false
         )
 
-        return await coredataContext.perform {
+        return try await coredataContext.perform {
             guard let carbEntries = results as? [CarbEntryStored] else {
-                return []
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return carbEntries.map { result in
@@ -387,8 +387,10 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             ascending: false
         )
 
-        return await coredataContext.perform {
-            guard let fpuEntries = results as? [CarbEntryStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fpuEntries = results as? [CarbEntryStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fpuEntries.map { result in
                 NightscoutTreatment(
@@ -424,11 +426,11 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             ascending: false
         )
 
-        guard let carbEntries = results as? [CarbEntryStored] else {
-            return []
-        }
+        return try await coredataContext.perform {
+            guard let carbEntries = results as? [CarbEntryStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return carbEntries.map { result in
                 CarbsEntry(
                     id: result.id?.uuidString,
@@ -455,11 +457,11 @@ final class BaseCarbsStorage: CarbsStorage, Injectable {
             ascending: false
         )
 
-        guard let carbEntries = results as? [CarbEntryStored] else {
-            return []
-        }
+        return try await coredataContext.perform {
+            guard let carbEntries = results as? [CarbEntryStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return carbEntries.map { result in
                 CarbsEntry(
                     id: result.id?.uuidString,

+ 4 - 2
Trio/Sources/APS/Storage/ContactImageStorage.swift

@@ -35,8 +35,10 @@ final class BaseContactImageStorage: ContactImageStorage, Injectable {
                 ascending: false
             )
 
-            return await backgroundContext.perform {
-                guard let fetchedContactImageEntries = results as? [ContactImageEntryStored] else { return [] }
+            return try await backgroundContext.perform {
+                guard let fetchedContactImageEntries = results as? [ContactImageEntryStored]
+                else { throw CoreDataError.fetchError(function: #function, file: #file)
+                }
 
                 return fetchedContactImageEntries.compactMap { entry in
                     ContactImageEntry(

+ 4 - 2
Trio/Sources/APS/Storage/DeterminationStorage.swift

@@ -35,8 +35,10 @@ final class BaseDeterminationStorage: DeterminationStorage, Injectable {
             fetchLimit: 1
         )
 
-        return await context.perform {
-            guard let fetchedResults = results as? [OrefDetermination] else { return [] }
+        return try await context.perform {
+            guard let fetchedResults = results as? [OrefDetermination] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }

+ 24 - 12
Trio/Sources/APS/Storage/GlucoseStorage.swift

@@ -336,8 +336,10 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             ascending: false
         )
 
-        return await coredataContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map { result in
                 BloodGlucose(
@@ -367,9 +369,11 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return fetchedResults.map { result in
                 NightscoutTreatment(
                     duration: nil,
@@ -421,9 +425,11 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return fetchedResults.map { result in
                 BloodGlucose(
                     _id: result.id?.uuidString ?? UUID().uuidString,
@@ -451,9 +457,11 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return fetchedResults.map { result in
                 BloodGlucose(
                     _id: result.id?.uuidString ?? UUID().uuidString,
@@ -481,9 +489,11 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return fetchedResults.map { result in
                 BloodGlucose(
                     _id: result.id?.uuidString ?? UUID().uuidString,
@@ -512,9 +522,11 @@ final class BaseGlucoseStorage: GlucoseStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await coredataContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await coredataContext.perform {
             return fetchedResults.map { result in
                 BloodGlucose(
                     _id: result.id?.uuidString ?? UUID().uuidString,

+ 28 - 14
Trio/Sources/APS/Storage/OverrideStorage.swift

@@ -47,8 +47,10 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             fetchLimit: 1
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [OverrideStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }
@@ -64,8 +66,10 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             fetchLimit: fetchLimit
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [OverrideStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }
@@ -81,8 +85,10 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             ascending: true
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [OverrideStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }
@@ -209,8 +215,10 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             ascending: false
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedOverrides = results as? [OverrideStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedOverrides = results as? [OverrideStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedOverrides.map { override in
                 let duration = override.indefinite ? 43200 : override.duration ?? 0 // 43200 min = 30 days
@@ -239,8 +247,10 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             ascending: false
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedOverrideRuns = results as? [OverrideRunStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedOverrideRuns = results as? [OverrideRunStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedOverrideRuns.map { overrideRun in
                 var durationInMinutes = (overrideRun.endDate?.timeIntervalSince(overrideRun.startDate ?? Date()) ?? 1) / 60
@@ -266,8 +276,10 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             ascending: true
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [OverrideStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map { overrideStored in
                 let duration = overrideStored.duration as? Decimal != 0 ? overrideStored.duration as? Decimal : nil
@@ -294,10 +306,12 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
             fetchLimit: 1
         )
 
-        return await backgroundContext.perform {
+        return try await backgroundContext.perform {
             guard let fetchedResults = results as? [OverrideStored],
                   let latestOverride = fetchedResults.first
-            else { return nil }
+            else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return latestOverride.objectID
         }

+ 15 - 8
Trio/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -216,6 +216,7 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
                 debugPrint("\(DebuggingIdentifiers.succeeded) stored pump events in Core Data")
             } catch let error as NSError {
                 debugPrint("\(DebuggingIdentifiers.failed) failed to store pump events with error: \(error.userInfo)")
+                throw error
             }
         }
     }
@@ -270,8 +271,10 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
             ascending: false
         )
 
-        return await context.perform { [self] in
-            guard let fetchedPumpEvents = results as? [PumpEventStored] else { return [] }
+        return try await context.perform { [self] in
+            guard let fetchedPumpEvents = results as? [PumpEventStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedPumpEvents.map { event in
                 switch event.type {
@@ -429,10 +432,12 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedPumpEvents = results as? [PumpEventStored] else { return [] }
+        return try await context.perform {
+            guard let fetchedPumpEvents = results as? [PumpEventStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await context.perform {
-            fetchedPumpEvents.map { event in
+            return fetchedPumpEvents.map { event in
                 switch event.type {
                 case PumpEvent.bolus.rawValue:
                     return PumpHistoryEvent(
@@ -471,10 +476,12 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedPumpEvents = results as? [PumpEventStored] else { return [] }
+        return try await context.perform {
+            guard let fetchedPumpEvents = results as? [PumpEventStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await context.perform {
-            fetchedPumpEvents.map { event in
+            return fetchedPumpEvents.map { event in
                 switch event.type {
                 case PumpEvent.bolus.rawValue:
                     return PumpHistoryEvent(

+ 58 - 48
Trio/Sources/APS/Storage/TempTargetsStorage.swift

@@ -8,7 +8,7 @@ protocol TempTargetsObserver {
 }
 
 protocol TempTargetsStorage {
-    func storeTempTarget(tempTarget: TempTarget) async
+    func storeTempTarget(tempTarget: TempTarget) async throws
     func saveTempTargetsToStorage(_ targets: [TempTarget])
     func fetchForTempTargetPresets() async throws -> [NSManagedObjectID]
     func fetchScheduledTempTargets() async throws -> [NSManagedObjectID]
@@ -48,9 +48,11 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
             fetchLimit: fetchLimit
         )
 
-        guard let fetchedResults = results as? [TempTargetStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [TempTargetStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await backgroundContext.perform {
             return fetchedResults.map(\.objectID)
         }
     }
@@ -65,9 +67,11 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
             ascending: true
         )
 
-        guard let fetchedResults = results as? [TempTargetStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [TempTargetStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await backgroundContext.perform {
             return fetchedResults.map(\.objectID)
         }
     }
@@ -83,9 +87,11 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
             ascending: false
         )
 
-        guard let fetchedResults = results as? [TempTargetStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [TempTargetStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await backgroundContext.perform {
             return fetchedResults.map(\.objectID)
         }
     }
@@ -102,53 +108,53 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
             fetchLimit: 1
         )
 
-        guard let fetchedResults = results as? [TempTargetStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [TempTargetStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
-        return await backgroundContext.perform {
-            fetchedResults.map(\.objectID)
+            return fetchedResults.map(\.objectID)
         }
     }
 
-    func storeTempTarget(tempTarget: TempTarget) async {
-        do {
-            var presetCount = -1
-            if tempTarget.isPreset == true {
-                let presets = try await fetchForTempTargetPresets()
-                presetCount = presets.count
+    func storeTempTarget(tempTarget: TempTarget) async throws {
+        var presetCount = -1
+        if tempTarget.isPreset == true {
+            let presets = try await fetchForTempTargetPresets()
+            presetCount = presets.count
+        }
+
+        try await backgroundContext.perform {
+            let newTempTarget = TempTargetStored(context: self.backgroundContext)
+            newTempTarget.date = tempTarget.createdAt
+            newTempTarget.id = UUID()
+            newTempTarget.enabled = tempTarget.enabled ?? false
+            newTempTarget.duration = tempTarget.duration as NSDecimalNumber
+            newTempTarget.isUploadedToNS = false
+            newTempTarget.name = tempTarget.name
+            newTempTarget.target = NSDecimalNumber(decimal: tempTarget.targetTop ?? 0)
+            newTempTarget.isPreset = tempTarget.isPreset ?? false
+
+            // Nullify half basal target to ensure the latest HBT is used via OpenAPS Manager when sending TT data to oref
+            newTempTarget.halfBasalTarget = nil
+
+            if let halfBasalTarget = tempTarget.halfBasalTarget,
+               halfBasalTarget != self.settingsManager.preferences.halfBasalExerciseTarget
+            {
+                newTempTarget.halfBasalTarget = NSDecimalNumber(decimal: halfBasalTarget)
             }
 
-            try await backgroundContext.perform {
-                let newTempTarget = TempTargetStored(context: self.backgroundContext)
-                newTempTarget.date = tempTarget.createdAt
-                newTempTarget.id = UUID()
-                newTempTarget.enabled = tempTarget.enabled ?? false
-                newTempTarget.duration = tempTarget.duration as NSDecimalNumber
-                newTempTarget.isUploadedToNS = false
-                newTempTarget.name = tempTarget.name
-                newTempTarget.target = NSDecimalNumber(decimal: tempTarget.targetTop ?? 0)
-                newTempTarget.isPreset = tempTarget.isPreset ?? false
-
-                // Nullify half basal target to ensure the latest HBT is used via OpenAPS Manager when sending TT data to oref
-                newTempTarget.halfBasalTarget = nil
-
-                if let halfBasalTarget = tempTarget.halfBasalTarget,
-                   halfBasalTarget != self.settingsManager.preferences.halfBasalExerciseTarget
-                {
-                    newTempTarget.halfBasalTarget = NSDecimalNumber(decimal: halfBasalTarget)
-                }
-
-                if tempTarget.isPreset == true, presetCount > -1 {
-                    newTempTarget.orderPosition = Int16(presetCount + 1)
-                }
+            if tempTarget.isPreset == true, presetCount > -1 {
+                newTempTarget.orderPosition = Int16(presetCount + 1)
+            }
 
+            do {
                 guard self.backgroundContext.hasChanges else { return }
                 try self.backgroundContext.save()
+            } catch let error as NSError {
+                debug(.default, "\(DebuggingIdentifiers.failed) Failed to save new temp target with error: \(error.userInfo)")
+                throw error
             }
-        } catch {
-            debug(
-                .default,
-                "\(DebuggingIdentifiers.failed) Failed to store temp target: \(error.localizedDescription)"
-            )
         }
     }
 
@@ -252,8 +258,10 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
             ascending: false
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedTempTargets = results as? [TempTargetStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedTempTargets = results as? [TempTargetStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedTempTargets.map { tempTarget in
                 NightscoutTreatment(
@@ -291,8 +299,10 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
             ascending: false
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedTempTargetRuns = results as? [TempTargetRunStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedTempTargetRuns = results as? [TempTargetRunStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedTempTargetRuns.map { tempTargetRun in
                 var durationInMinutes = (tempTargetRun.endDate?.timeIntervalSince(tempTargetRun.startDate ?? Date()) ?? 1) / 60

+ 9 - 2
Trio/Sources/Application/AppDelegate.swift

@@ -23,8 +23,15 @@ class AppDelegate: NSObject, UIApplicationDelegate, ObservableObject, UNUserNoti
             let pushMessage = try JSONDecoder().decode(PushMessage.self, from: jsonData)
 
             Task {
-                try await TrioRemoteControl.shared.handleRemoteNotification(pushMessage: pushMessage)
-                completionHandler(.newData)
+                do {
+                    try await TrioRemoteControl.shared.handleRemoteNotification(pushMessage: pushMessage)
+                    completionHandler(.newData)
+                } catch {
+                    debug(
+                        .default,
+                        "\(DebuggingIdentifiers.failed) failed to handle remote notification with error: \(error.localizedDescription)"
+                    )
+                }
             }
         } catch {
             debug(.remoteControl, "Error decoding push message: \(error.localizedDescription)")

+ 24 - 20
Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+TempTargets.swift

@@ -11,10 +11,17 @@ extension Adjustments.StateModel {
     /// This also needs to be called when we cancel an Temp Target via the Home View to update the State of the Button for this case
     func updateLatestTempTargetConfiguration() {
         Task {
-            let id = try await tempTargetStorage.loadLatestTempTargetConfigurations(fetchLimit: 1)
-            async let updateState: () = updateLatestTempTargetConfigurationOfState(from: id)
-            async let setTempTarget: () = setCurrentTempTarget(from: id)
-            _ = await (updateState, setTempTarget)
+            do {
+                let id = try await tempTargetStorage.loadLatestTempTargetConfigurations(fetchLimit: 1)
+                async let updateState: () = updateLatestTempTargetConfigurationOfState(from: id)
+                async let setTempTarget: () = setCurrentTempTarget(from: id)
+                _ = await (updateState, setTempTarget)
+            } catch {
+                debug(
+                    .default,
+                    "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to load latest temp target configuration with error: \(error.localizedDescription)"
+                )
+            }
         }
     }
 
@@ -115,16 +122,16 @@ extension Adjustments.StateModel {
     }
 
     /// Saves a Temp Target based on whether it is scheduled or custom.
-    func invokeSaveOfCustomTempTargets() async {
+    func invokeSaveOfCustomTempTargets() async throws {
         if date > Date() {
-            await saveScheduledTempTarget()
+            try await saveScheduledTempTarget()
         } else {
-            await saveCustomTempTarget()
+            try await saveCustomTempTarget()
         }
     }
 
     /// Saves a scheduled Temp Target and activates it at the specified date.
-    func saveScheduledTempTarget() async {
+    func saveScheduledTempTarget() async throws {
         let date = self.date
         guard date > Date() else { return }
 
@@ -140,15 +147,12 @@ extension Adjustments.StateModel {
             enabled: false,
             halfBasalTarget: halfBasalTarget
         )
-        await tempTargetStorage.storeTempTarget(tempTarget: tempTarget)
+        try await tempTargetStorage.storeTempTarget(tempTarget: tempTarget)
         setupScheduledTempTargetsArray()
-
-        Task {
-            await waitUntilDate(date)
-            await disableAllActiveTempTargets(createTempTargetRunEntry: true)
-            await enableScheduledTempTarget(for: date)
-            tempTargetStorage.saveTempTargetsToStorage([tempTarget])
-        }
+        await waitUntilDate(date)
+        await disableAllActiveTempTargets(createTempTargetRunEntry: true)
+        await enableScheduledTempTarget(for: date)
+        tempTargetStorage.saveTempTargetsToStorage([tempTarget])
     }
 
     /// Enables a scheduled Temp Target for a specific date.
@@ -194,7 +198,7 @@ extension Adjustments.StateModel {
     }
 
     /// Saves a custom Temp Target and disables existing ones.
-    func saveCustomTempTarget() async {
+    func saveCustomTempTarget() async throws {
         await disableAllActiveTempTargets(createTempTargetRunEntry: true)
         let tempTarget = TempTarget(
             name: tempTargetName,
@@ -209,7 +213,7 @@ extension Adjustments.StateModel {
             enabled: true,
             halfBasalTarget: halfBasalTarget
         )
-        await tempTargetStorage.storeTempTarget(tempTarget: tempTarget)
+        try await tempTargetStorage.storeTempTarget(tempTarget: tempTarget)
         tempTargetStorage.saveTempTargetsToStorage([tempTarget])
         await resetTempTargetState()
         isTempTargetEnabled = true
@@ -217,7 +221,7 @@ extension Adjustments.StateModel {
     }
 
     /// Creates a new Temp Target preset.
-    func saveTempTargetPreset() async {
+    func saveTempTargetPreset() async throws {
         let tempTarget = TempTarget(
             name: tempTargetName,
             createdAt: Date(),
@@ -230,7 +234,7 @@ extension Adjustments.StateModel {
             enabled: false,
             halfBasalTarget: halfBasalTarget
         )
-        await tempTargetStorage.storeTempTarget(tempTarget: tempTarget)
+        try await tempTargetStorage.storeTempTarget(tempTarget: tempTarget)
         await resetTempTargetState()
         setupTempTargetPresetsArray()
     }

+ 11 - 7
Trio/Sources/Modules/Adjustments/AdjustmentsStateModel.swift

@@ -170,13 +170,17 @@ extension Adjustments {
             for (index, override) in overridePresets.enumerated() {
                 override.orderPosition = Int16(index + 1)
             }
-            do {
-                guard viewContext.hasChanges else { return }
-                try viewContext.save()
-                setupOverridePresetsArray()
-                Task { try await nightscoutManager.uploadProfiles() }
-            } catch {
-                debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to save Override Presets order")
+            Task {
+                do {
+                    guard viewContext.hasChanges else { return }
+                    try viewContext.save()
+                    setupOverridePresetsArray()
+                    try await nightscoutManager.uploadProfiles()
+                } catch {
+                    debugPrint(
+                        "\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to save Override Presets order or upload profiles"
+                    )
+                }
             }
         }
 

+ 22 - 20
Trio/Sources/Modules/Adjustments/View/Overrides/EditOverrideForm.swift

@@ -513,29 +513,31 @@ struct EditOverrideForm: View {
                 Button(action: {
                     saveChanges()
 
-                    do {
-                        guard let moc = override.managedObjectContext else { return }
-                        guard moc.hasChanges else { return }
-                        try moc.save()
-                        Task {
+                    Task {
+                        do {
+                            guard let moc = override.managedObjectContext else { return }
+                            guard moc.hasChanges else { return }
+                            try moc.save()
+
                             try await state.nightscoutManager.uploadProfiles()
-                        }
-                        // Disable previous active Override
-                        if let currentActiveOverride = state.currentActiveOverride {
-                            Task {
-                                await state.disableAllActiveOverrides(
-                                    except: currentActiveOverride.objectID,
-                                    createOverrideRunEntry: false
-                                )
-                                // Update View
-                                state.updateLatestOverrideConfiguration()
+
+                            // Disable previous active Override
+                            if let currentActiveOverride = state.currentActiveOverride {
+                                Task {
+                                    await state.disableAllActiveOverrides(
+                                        except: currentActiveOverride.objectID,
+                                        createOverrideRunEntry: false
+                                    )
+                                    // Update View
+                                    state.updateLatestOverrideConfiguration()
+                                }
                             }
-                        }
 
-                        hasChanges = false
-                        presentationMode.wrappedValue.dismiss()
-                    } catch {
-                        debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to edit Override")
+                            hasChanges = false
+                            presentationMode.wrappedValue.dismiss()
+                        } catch {
+                            debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to edit Override")
+                        }
                     }
                 }, label: {
                     Text("Save Override")

+ 16 - 8
Trio/Sources/Modules/Adjustments/View/TempTargets/AddTempTargetForm.swift

@@ -249,10 +249,14 @@ struct AddTempTargetForm: View {
                 content: {
                     Button(action: {
                         Task {
-                            if noNameSpecified { state.tempTargetName = "Custom Target" }
-                            didPressSave.toggle()
-                            await state.invokeSaveOfCustomTempTargets()
-                            dismiss()
+                            do {
+                                if noNameSpecified { state.tempTargetName = "Custom Target" }
+                                didPressSave.toggle()
+                                try await state.invokeSaveOfCustomTempTargets()
+                                dismiss()
+                            } catch {
+                                debug(.default, "\(DebuggingIdentifiers.failed) failed to save custom temp target: \(error)")
+                            }
                         }
                     }, label: {
                         Text("Start Temp Target")
@@ -266,10 +270,14 @@ struct AddTempTargetForm: View {
             Section {
                 Button(action: {
                     Task {
-                        if noNameSpecified { state.tempTargetName = "Custom Target" }
-                        didPressSave.toggle()
-                        await state.saveTempTargetPreset()
-                        dismiss()
+                        do {
+                            if noNameSpecified { state.tempTargetName = "Custom Target" }
+                            didPressSave.toggle()
+                            try await state.saveTempTargetPreset()
+                            dismiss()
+                        } catch {
+                            debug(.default, "\(DebuggingIdentifiers.failed) failed to save temp target preset: \(error)")
+                        }
                     }
                 }, label: {
                     Text("Save as Preset")

+ 9 - 2
Trio/Sources/Modules/AlgorithmAdvancedSettings/AlgorithmAdvancedSettingsStateModel.swift

@@ -66,8 +66,15 @@ extension AlgorithmAdvancedSettings {
                         self.insulinActionCurve = settings.insulinActionCurve
 
                         Task.detached(priority: .low) {
-                            debug(.nightscout, "Attempting to upload DIA to Nightscout")
-                            try await self.nightscout.uploadProfiles()
+                            do {
+                                debug(.nightscout, "Attempting to upload DIA to Nightscout")
+                                try await self.nightscout.uploadProfiles()
+                            } catch {
+                                debug(
+                                    .default,
+                                    "\(DebuggingIdentifiers.failed) failed to upload DIA to Nightscout: \(error.localizedDescription)"
+                                )
+                            }
                         }
                     } receiveValue: {}
                     .store(in: &lifetime)

+ 6 - 2
Trio/Sources/Modules/BasalProfileEditor/BasalProfileEditorStateModel.swift

@@ -94,8 +94,12 @@ extension BasalProfileEditor {
                         self.initialItems = self.items.map { Item(rateIndex: $0.rateIndex, timeIndex: $0.timeIndex) }
 
                         Task.detached(priority: .low) {
-                            debug(.nightscout, "Attempting to upload basal rates to Nightscout")
-                            try await self.nightscout.uploadProfiles()
+                            do {
+                                debug(.nightscout, "Attempting to upload basal rates to Nightscout")
+                                try await self.nightscout.uploadProfiles()
+                            } catch {
+                                debug(.default, "Failed to upload basal rates to Nightscout: \(error.localizedDescription)")
+                            }
                         }
                     case .failure:
                         // Handle the error, show error message

+ 2 - 2
Trio/Sources/Modules/Calibrations/CalibrationsStateModel.swift

@@ -46,9 +46,9 @@ extension Calibrations {
                 fetchLimit: 1 /// We only need the last value
             )
 
-            return await backgroundContext.perform {
+            return try await backgroundContext.perform {
                 guard let glucoseResults = results as? [GlucoseStored] else {
-                    return []
+                    throw CoreDataError.fetchError(function: #function, file: #file)
                 }
 
                 return glucoseResults.map(\.objectID)

+ 6 - 2
Trio/Sources/Modules/CarbRatioEditor/CarbRatioEditorStateModel.swift

@@ -70,8 +70,12 @@ extension CarbRatioEditor {
             provider.saveProfile(profile)
             initialItems = items.map { Item(rateIndex: $0.rateIndex, timeIndex: $0.timeIndex) }
             Task.detached(priority: .low) {
-                debug(.nightscout, "Attempting to upload CRs to Nightscout")
-                try await self.nightscout.uploadProfiles()
+                do {
+                    debug(.nightscout, "Attempting to upload CRs to Nightscout")
+                    try await self.nightscout.uploadProfiles()
+                } catch {
+                    debug(.default, "Failed to upload CRs to Nightscout: \(error.localizedDescription)")
+                }
             }
         }
 

+ 28 - 22
Trio/Sources/Modules/DataTable/DataTableStateModel.swift

@@ -299,30 +299,36 @@ extension DataTable {
             newDate: Date
         ) {
             Task {
-                // Get original date from entry to re-create the entry later with the updated values and the same date
-                guard let originalEntry = await getOriginalEntryValues(treatmentObjectID) else { return }
-
-                // Deletion logic for carb and FPU entries
-                try await deleteOldEntries(
-                    treatmentObjectID,
-                    originalEntry: originalEntry,
-                    newCarbs: newCarbs,
-                    newFat: newFat,
-                    newProtein: newProtein,
-                    newNote: newNote
-                )
+                do {
+                    // Get original date from entry to re-create the entry later with the updated values and the same date
+                    guard let originalEntry = await getOriginalEntryValues(treatmentObjectID) else { return }
+
+                    // Deletion logic for carb and FPU entries
+                    try await deleteOldEntries(
+                        treatmentObjectID,
+                        originalEntry: originalEntry,
+                        newCarbs: newCarbs,
+                        newFat: newFat,
+                        newProtein: newProtein,
+                        newNote: newNote
+                    )
 
-                try await createNewEntries(
-                    originalDate: newDate,
-                    newCarbs: newCarbs,
-                    newFat: newFat,
-                    newProtein: newProtein,
-                    newNote: newNote
-                )
+                    try await createNewEntries(
+                        originalDate: newDate,
+                        newCarbs: newCarbs,
+                        newFat: newFat,
+                        newProtein: newProtein,
+                        newNote: newNote
+                    )
 
-                await syncWithServices()
-                // Perform a determine basal sync to update cob
-                try await apsManager.determineBasalSync()
+                    await syncWithServices()
+
+                    // Perform a determine basal sync to update cob
+                    try await apsManager.determineBasalSync()
+
+                } catch {
+                    debug(.default, "\(DebuggingIdentifiers.failed) failed to update entry: \(error.localizedDescription)")
+                }
             }
         }
 

+ 4 - 2
Trio/Sources/Modules/Home/HomeStateModel+Setup/BatterySetup.swift

@@ -27,8 +27,10 @@ extension Home.StateModel {
             ascending: false
         )
 
-        return await batteryFetchContext.perform {
-            guard let fetchedResults = results as? [OpenAPS_Battery] else { return [] }
+        return try await batteryFetchContext.perform {
+            guard let fetchedResults = results as? [OpenAPS_Battery] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }

+ 8 - 4
Trio/Sources/Modules/Home/HomeStateModel+Setup/CarbSetup.swift

@@ -25,8 +25,10 @@ extension Home.StateModel {
             batchSize: 5
         )
 
-        return await carbsFetchContext.perform {
-            guard let fetchedResults = results as? [CarbEntryStored] else { return [] }
+        return try await carbsFetchContext.perform {
+            guard let fetchedResults = results as? [CarbEntryStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }
@@ -58,8 +60,10 @@ extension Home.StateModel {
             ascending: false
         )
 
-        return await fpuFetchContext.perform {
-            guard let fetchedResults = results as? [CarbEntryStored] else { return [] }
+        return try await fpuFetchContext.perform {
+            guard let fetchedResults = results as? [CarbEntryStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }

+ 2 - 2
Trio/Sources/Modules/Home/HomeStateModel+Setup/DeterminationSetup.swift

@@ -50,9 +50,9 @@ extension Home.StateModel {
             propertiesToFetch: ["cob", "iob", "deliverAt", "objectID"]
         )
 
-        return await determinationFetchContext.perform {
+        return try await determinationFetchContext.perform {
             guard let fetchedResults = results as? [[String: Any]] else {
-                return []
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             // Update Chart Scales

+ 4 - 2
Trio/Sources/Modules/Home/HomeStateModel+Setup/GlucoseSetup.swift

@@ -28,8 +28,10 @@ extension Home.StateModel {
             batchSize: 50
         )
 
-        return await glucoseFetchContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await glucoseFetchContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             // Update Main Chart Y Axis Values
             // Perform everything on "context" to be thread safe

+ 8 - 4
Trio/Sources/Modules/Home/HomeStateModel+Setup/OverrideSetup.swift

@@ -27,8 +27,10 @@ extension Home.StateModel {
             ascending: false
         )
 
-        return await overrideFetchContext.perform {
-            guard let fetchedResults = results as? [OverrideStored] else { return [] }
+        return try await overrideFetchContext.perform {
+            guard let fetchedResults = results as? [OverrideStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }
@@ -70,8 +72,10 @@ extension Home.StateModel {
             ascending: false
         )
 
-        return await overrideFetchContext.perform {
-            guard let fetchedResults = results as? [OverrideRunStored] else { return [] }
+        return try await overrideFetchContext.perform {
+            guard let fetchedResults = results as? [OverrideRunStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }

+ 6 - 4
Trio/Sources/Modules/Home/HomeStateModel+Setup/PumpHistorySetup.swift

@@ -28,9 +28,9 @@ extension Home.StateModel {
             batchSize: 30
         )
 
-        return await pumpHistoryFetchContext.perform {
+        return try await pumpHistoryFetchContext.perform {
             guard let pumpEvents = results as? [PumpEventStored] else {
-                return []
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return pumpEvents.map(\.objectID)
@@ -78,8 +78,10 @@ extension Home.StateModel {
             fetchLimit: 1
         )
 
-        return await pumpHistoryFetchContext.perform {
-            guard let fetchedResults = results as? [PumpEventStored] else { return [].first }
+        return try await pumpHistoryFetchContext.perform {
+            guard let fetchedResults = results as? [PumpEventStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID).first
         }

+ 8 - 4
Trio/Sources/Modules/Home/HomeStateModel+Setup/TempTargetSetup.swift

@@ -27,8 +27,10 @@ extension Home.StateModel {
             ascending: false
         )
 
-        return await tempTargetFetchContext.perform {
-            guard let fetchedResults = results as? [TempTargetStored] else { return [] }
+        return try await tempTargetFetchContext.perform {
+            guard let fetchedResults = results as? [TempTargetStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }
@@ -64,8 +66,10 @@ extension Home.StateModel {
             ascending: false
         )
 
-        return await tempTargetFetchContext.perform {
-            guard let fetchedResults = results as? [TempTargetRunStored] else { return [] }
+        return try await tempTargetFetchContext.perform {
+            guard let fetchedResults = results as? [TempTargetRunStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }

+ 9 - 2
Trio/Sources/Modules/ISFEditor/ISFEditorStateModel.swift

@@ -85,8 +85,15 @@ extension ISFEditor {
             initialItems = items.map { Item(rateIndex: $0.rateIndex, timeIndex: $0.timeIndex) }
 
             Task.detached(priority: .low) {
-                debug(.nightscout, "Attempting to upload ISF to Nightscout")
-                try await self.nightscout.uploadProfiles()
+                do {
+                    debug(.nightscout, "Attempting to upload ISF to Nightscout")
+                    try await self.nightscout.uploadProfiles()
+                } catch {
+                    debug(
+                        .default,
+                        "\(DebuggingIdentifiers.failed) Faile to upload ISF to Nightscout: \(error.localizedDescription)"
+                    )
+                }
             }
         }
 

+ 17 - 3
Trio/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -75,7 +75,14 @@ extension NightscoutConfig {
                     if enabled {
                         debug(.nightscout, "Upload has been enabled by the user.")
                         Task {
-                            try await self.nightscoutManager.uploadProfiles()
+                            do {
+                                try await self.nightscoutManager.uploadProfiles()
+                            } catch {
+                                debug(
+                                    .default,
+                                    "\(DebuggingIdentifiers.failed) failed to upload profiles: \(error.localizedDescription)"
+                                )
+                            }
                         }
                     } else {
                         debug(.nightscout, "Upload has been disabled by the user.")
@@ -408,8 +415,15 @@ extension NightscoutConfig {
                         self.importedInsulinActionCurve = settings.insulinActionCurve
 
                         Task.detached(priority: .low) {
-                            debug(.nightscout, "Attempting to upload DIA to Nightscout after import review")
-                            try await self.nightscoutManager.uploadProfiles()
+                            do {
+                                debug(.nightscout, "Attempting to upload DIA to Nightscout after import review")
+                                try await self.nightscoutManager.uploadProfiles()
+                            } catch {
+                                debug(
+                                    .default,
+                                    "\(DebuggingIdentifiers.failed) failed to upload DIA to Nightscout: \(error.localizedDescription)"
+                                )
+                            }
                         }
                     } receiveValue: {}
                     .store(in: &lifetime)

+ 4 - 2
Trio/Sources/Modules/Stat/StatStateModel.swift

@@ -72,8 +72,10 @@ extension Stat {
                     propertiesToFetch: ["glucose", "objectID"]
                 )
 
-                return await context.perform {
-                    guard let fetchedResults = results as? [[String: Any]] else { return [] }
+                return try await context.perform {
+                    guard let fetchedResults = results as? [[String: Any]] else {
+                        throw CoreDataError.fetchError(function: #function, file: #file)
+                    }
                     return fetchedResults.compactMap { $0["objectID"] as? NSManagedObjectID }
                 }
             } catch {

+ 9 - 2
Trio/Sources/Modules/TargetsEditor/TargetsEditorStateModel.swift

@@ -83,8 +83,15 @@ extension TargetsEditor {
             }
 
             Task.detached(priority: .low) {
-                debug(.nightscout, "Attempting to upload targets to Nightscout")
-                try await self.nightscout.uploadProfiles()
+                do {
+                    debug(.nightscout, "Attempting to upload targets to Nightscout")
+                    try await self.nightscout.uploadProfiles()
+                } catch {
+                    debug(
+                        .default,
+                        "\(DebuggingIdentifiers.failed) failed to upload targets to Nightscout: \(error.localizedDescription)"
+                    )
+                }
             }
         }
 

+ 4 - 2
Trio/Sources/Modules/Treatments/TreatmentsStateModel.swift

@@ -681,8 +681,10 @@ extension Treatments.StateModel {
             ascending: false
         )
 
-        return await glucoseFetchContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await glucoseFetchContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }

+ 4 - 2
Trio/Sources/Services/BolusCalculator/BolusCalculationManager.swift

@@ -201,8 +201,10 @@ final class BaseBolusCalculationManager: BolusCalculationManager, Injectable {
             fetchLimit: 288
         )
 
-        return await glucoseFetchContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await glucoseFetchContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }

+ 8 - 4
Trio/Sources/Services/Calendar/CalendarManager.swift

@@ -186,8 +186,10 @@ final class BaseCalendarManager: CalendarManager, Injectable {
             propertiesToFetch: ["timestamp", "cob", "iob", "objectID"]
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [[String: Any]] else { return nil }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [[String: Any]] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.first?["objectID"] as? NSManagedObjectID
         }
@@ -203,8 +205,10 @@ final class BaseCalendarManager: CalendarManager, Injectable {
             propertiesToFetch: ["objectID", "glucose"]
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [[String: Any]] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [[String: Any]] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.compactMap { $0["objectID"] as? NSManagedObjectID }
         }

+ 6 - 4
Trio/Sources/Services/ContactImage/ContactImageManager.swift

@@ -98,8 +98,10 @@ final class BaseContactImageManager: NSObject, ContactImageManager, Injectable {
             fetchLimit: 1
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [OrefDetermination] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [OrefDetermination] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }
@@ -115,9 +117,9 @@ final class BaseContactImageManager: NSObject, ContactImageManager, Injectable {
             fetchLimit: 3 /// We only need 1-3 values, depending on whether the user wants to show delta or not
         )
 
-        return await backgroundContext.perform {
+        return try await backgroundContext.perform {
             guard let glucoseResults = results as? [GlucoseStored] else {
-                return []
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return glucoseResults.map(\.objectID)

+ 4 - 2
Trio/Sources/Services/HealthKit/HealthKitManager.swift

@@ -391,8 +391,10 @@ final class BaseHealthKitManager: HealthKitManager, Injectable {
 
             var insulinSamples: [HKQuantitySample] = []
 
-            await backgroundContext.perform {
-                guard let existingTempBasalEntries = fetchedInsulinEntries as? [PumpEventStored] else { return }
+            try await backgroundContext.perform {
+                guard let existingTempBasalEntries = fetchedInsulinEntries as? [PumpEventStored] else {
+                    throw CoreDataError.fetchError(function: #function, file: #file)
+                }
 
                 for event in insulinEvents {
                     switch event.type {

+ 6 - 6
Trio/Sources/Services/LiveActivity/Data/DataManager.swift

@@ -14,9 +14,9 @@ extension LiveActivityBridge {
             fetchLimit: 72
         )
 
-        return await context.perform {
+        return try await context.perform {
             guard let glucoseResults = results as? [GlucoseStored] else {
-                return []
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return glucoseResults.map {
@@ -36,9 +36,9 @@ extension LiveActivityBridge {
             propertiesToFetch: ["iob", "cob", "totalDailyDose", "currentTarget", "deliverAt"]
         )
 
-        return await context.perform {
+        return try await context.perform {
             guard let determinationResults = results as? [[String: Any]] else {
-                return nil
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return determinationResults.first.map {
@@ -64,9 +64,9 @@ extension LiveActivityBridge {
             propertiesToFetch: ["enabled", "name", "target", "date", "duration"]
         )
 
-        return await context.perform {
+        return try await context.perform {
             guard let overrideResults = results as? [[String: Any]] else {
-                return nil
+                throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
             return overrideResults.first.map {

+ 23 - 8
Trio/Sources/Services/LiveActivity/LiveActivityBridge.swift

@@ -128,18 +128,29 @@ final class LiveActivityBridge: Injectable, ObservableObject, SettingsObserver {
 
     private func cobOrIobDidUpdate() {
         Task { @MainActor in
-            self.determination = try await fetchAndMapDetermination()
-            if let determination = determination {
-                await self.updateContentState(determination)
+            do {
+                self.determination = try await fetchAndMapDetermination()
+                if let determination = determination {
+                    await self.updateContentState(determination)
+                }
+            } catch {
+                debug(
+                    .default,
+                    "\(DebuggingIdentifiers.failed) failed to fetch and map determination: \(error.localizedDescription)"
+                )
             }
         }
     }
 
     private func overridesDidUpdate() {
         Task { @MainActor in
-            self.override = try await fetchAndMapOverride()
-            if let determination = determination {
-                await self.updateContentState(determination)
+            do {
+                self.override = try await fetchAndMapOverride()
+                if let determination = determination {
+                    await self.updateContentState(determination)
+                }
+            } catch {
+                debug(.default, "\(DebuggingIdentifiers.failed) failed to fetch and map override: \(error.localizedDescription)")
             }
         }
     }
@@ -198,8 +209,12 @@ final class LiveActivityBridge: Injectable, ObservableObject, SettingsObserver {
 
     private func setupGlucoseArray() {
         Task { @MainActor in
-            self.glucoseFromPersistence = try await fetchAndMapGlucose()
-            glucoseDidUpdate(glucoseFromPersistence ?? [])
+            do {
+                self.glucoseFromPersistence = try await fetchAndMapGlucose()
+                glucoseDidUpdate(glucoseFromPersistence ?? [])
+            } catch {
+                debug(.default, "\(DebuggingIdentifiers.failed) failed to fetch glucose with error: \(error)")
+            }
         }
     }
 

+ 16 - 10
Trio/Sources/Services/Network/Nightscout/NightscoutManager.swift

@@ -104,11 +104,18 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         /// This way, we ensure the latest enacted determination is always part of `devicestatus` and avoid having instances
         /// where the first uploaded non-enacted determination (i.e., "suggested"), lacks the "enacted" data.
         Task {
-            async let lastEnactedDeterminationID = determinationStorage
-                .fetchLastDeterminationObjectID(predicate: NSPredicate.enactedDetermination)
+            do {
+                let lastEnactedDeterminationID = try await determinationStorage
+                    .fetchLastDeterminationObjectID(predicate: NSPredicate.enactedDetermination)
 
-            self.lastEnactedDetermination = try await determinationStorage
-                .getOrefDeterminationNotYetUploadedToNightscout(lastEnactedDeterminationID)
+                self.lastEnactedDetermination = await determinationStorage
+                    .getOrefDeterminationNotYetUploadedToNightscout(lastEnactedDeterminationID)
+            } catch {
+                debug(
+                    .default,
+                    "\(DebuggingIdentifiers.failed) failed to fetch last enacted determination: \(error.localizedDescription)"
+                )
+            }
         }
     }
 
@@ -802,12 +809,11 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
                     return
                 }
 
-                do {
-                    try await nightscout.uploadProfile(profileStore)
-                    debug(.nightscout, "Profile uploaded")
-                } catch {
-                    debug(.nightscout, "NightscoutManager uploadProfile: \(error.localizedDescription)")
-                }
+                try await nightscout.uploadProfile(profileStore)
+                debug(.nightscout, "Profile uploaded")
+            } catch {
+                debug(.nightscout, "NightscoutManager uploadProfile: \(error.localizedDescription)")
+                throw error
             }
         } else {
             debug(.nightscout, "Upload to NS disabled; aborting profile uploaded")

+ 4 - 2
Trio/Sources/Services/Network/TidepoolManager.swift

@@ -304,8 +304,10 @@ extension BaseTidepoolManager {
             )
 
             // Ensure that the processing happens within the background context for thread safety
-            await backgroundContext.perform {
-                guard let existingTempBasalEntries = results as? [PumpEventStored] else { return }
+            try await backgroundContext.perform {
+                guard let existingTempBasalEntries = results as? [PumpEventStored] else {
+                    throw CoreDataError.fetchError(function: #function, file: #file)
+                }
 
                 let insulinDoseEvents: [DoseEntry] = events.reduce([]) { result, event in
                     var result = result

+ 2 - 2
Trio/Sources/Services/RemoteControl/TrioRemoteControl+Bolus.swift

@@ -74,7 +74,7 @@ extension TrioRemoteControl {
               let iob = firstResult["iob"] as? Decimal
         else {
             await logError("Failed to fetch current IOB.")
-            return Decimal(0)
+            throw CoreDataError.fetchError(function: #function, file: #file)
         }
 
         return iob
@@ -99,7 +99,7 @@ extension TrioRemoteControl {
 
         guard let bolusDictionaries = results as? [[String: Any]] else {
             await logError("Failed to cast fetched bolus events. Fetched entities type: \(type(of: results))")
-            return 0
+            throw CoreDataError.fetchError(function: #function, file: #file)
         }
 
         let totalAmount = bolusDictionaries.compactMap { ($0["bolus.amount"] as? NSNumber)?.decimalValue }.reduce(0, +)

+ 2 - 2
Trio/Sources/Services/RemoteControl/TrioRemoteControl+TempTarget.swift

@@ -2,7 +2,7 @@ import CoreData
 import Foundation
 
 extension TrioRemoteControl {
-    @MainActor func handleTempTargetCommand(_ pushMessage: PushMessage) async {
+    @MainActor func handleTempTargetCommand(_ pushMessage: PushMessage) async throws {
         guard let targetValue = pushMessage.target,
               let durationValue = pushMessage.duration
         else {
@@ -26,7 +26,7 @@ extension TrioRemoteControl {
             halfBasalTarget: settings.preferences.halfBasalExerciseTarget
         )
 
-        await tempTargetsStorage.storeTempTarget(tempTarget: tempTarget)
+        try await tempTargetsStorage.storeTempTarget(tempTarget: tempTarget)
         tempTargetsStorage.saveTempTargetsToStorage([tempTarget])
 
         debug(

+ 1 - 1
Trio/Sources/Services/RemoteControl/TrioRemoteControl.swift

@@ -69,7 +69,7 @@ class TrioRemoteControl: Injectable {
         case .bolus:
             try await handleBolusCommand(pushMessage)
         case .tempTarget:
-            await handleTempTargetCommand(pushMessage)
+            try await handleTempTargetCommand(pushMessage)
         case .cancelTempTarget:
             await cancelTempTarget(pushMessage)
         case .meal:

+ 4 - 2
Trio/Sources/Services/UserNotifications/UserNotificationsManager.swift

@@ -251,8 +251,10 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
             fetchLimit: 3
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }

+ 8 - 4
Trio/Sources/Services/WatchManager/AppleWatchManager.swift

@@ -347,8 +347,10 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
             fetchLimit: 288
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID)
         }
@@ -366,8 +368,10 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
             fetchLimit: 1
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [PumpEventStored] else { return [].first }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [PumpEventStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
 
             return fetchedResults.map(\.objectID).first
         }

+ 34 - 11
Trio/Sources/Services/WatchManager/GarminManager.swift

@@ -161,9 +161,16 @@ final class BaseGarminManager: NSObject, GarminManager, Injectable {
             .sink { [weak self] _ in
                 guard let self = self else { return }
                 Task {
-                    let watchState = try await self.setupGarminWatchState()
-                    let watchStateData = try JSONEncoder().encode(watchState)
-                    self.sendWatchStateData(watchStateData)
+                    do {
+                        let watchState = try await self.setupGarminWatchState()
+                        let watchStateData = try JSONEncoder().encode(watchState)
+                        self.sendWatchStateData(watchStateData)
+                    } catch {
+                        debug(
+                            .watchManager,
+                            "\(DebuggingIdentifiers.failed) failed to update watch state: \(error.localizedDescription)"
+                        )
+                    }
                 }
             }
             .store(in: &subscriptions)
@@ -174,9 +181,16 @@ final class BaseGarminManager: NSObject, GarminManager, Injectable {
             .sink { [weak self] _ in
                 guard let self = self else { return }
                 Task {
-                    let watchState = try await self.setupGarminWatchState()
-                    let watchStateData = try JSONEncoder().encode(watchState)
-                    self.sendWatchStateData(watchStateData)
+                    do {
+                        let watchState = try await self.setupGarminWatchState()
+                        let watchStateData = try JSONEncoder().encode(watchState)
+                        self.sendWatchStateData(watchStateData)
+                    } catch {
+                        debug(
+                            .watchManager,
+                            "\(DebuggingIdentifiers.failed) failed to update watch state: \(error.localizedDescription)"
+                        )
+                    }
                 }
             }
             .store(in: &subscriptions)
@@ -194,8 +208,10 @@ final class BaseGarminManager: NSObject, GarminManager, Injectable {
             fetchLimit: 288
         )
 
-        return await backgroundContext.perform {
-            guard let fetchedResults = results as? [GlucoseStored] else { return [] }
+        return try await backgroundContext.perform {
+            guard let fetchedResults = results as? [GlucoseStored] else {
+                throw CoreDataError.fetchError(function: #function, file: #file)
+            }
             return fetchedResults.map(\.objectID)
         }
     }
@@ -575,9 +591,16 @@ extension BaseGarminManager: SettingsObserver {
         units = settingsManager.settings.units
 
         Task {
-            let watchState = try await setupGarminWatchState()
-            let watchStateData = try JSONEncoder().encode(watchState)
-            sendWatchStateData(watchStateData)
+            do {
+                let watchState = try await setupGarminWatchState()
+                let watchStateData = try JSONEncoder().encode(watchState)
+                sendWatchStateData(watchStateData)
+            } catch {
+                debug(
+                    .watchManager,
+                    "\(DebuggingIdentifiers.failed) failed to send watch state data: \(error.localizedDescription)"
+                )
+            }
         }
     }
 }

+ 3 - 3
Trio/Sources/Shortcuts/TempPresets/TempPresetsIntentRequest.swift

@@ -13,7 +13,7 @@ final class TempPresetsIntentRequest: BaseIntentsRequest {
         let allTempTargetPresetsIDs = try await tempTargetsStorage.fetchForTempTargetPresets()
 
         // Perform the fetch and process on the Core Data context's thread
-        return await coredataContext.perform {
+        return try await coredataContext.perform {
             // Fetch existing TempTargetStored objects based on their NSManagedObjectIDs
             let tempTargetObjects: [TempTargetStored] = allTempTargetPresetsIDs.compactMap { id in
                 guard let object = try? self.coredataContext.existingObject(with: id) as? TempTargetStored else {
@@ -24,14 +24,14 @@ final class TempPresetsIntentRequest: BaseIntentsRequest {
             }
 
             // Map fetched TempTargetStored objects to TempPreset
-            return tempTargetObjects.compactMap { object in
+            return try tempTargetObjects.compactMap { object in
                 guard let id = object.id,
                       let name = object.name,
                       let target = object.target?.decimalValue,
                       let duration = object.duration?.decimalValue
                 else {
                     debugPrint("\(#file) \(#function) Missing data for TempTargetStored object.")
-                    return TempPreset(id: UUID(), name: "", duration: 0)
+                    throw TempPresetsError.noTempTargetFound
                 }
                 return TempPreset(id: id, name: name, targetTop: target, duration: duration)
             }