Przeglądaj źródła

Add ackCodes for localized ack msg handling; better override error handling

Deniz Cengiz 1 rok temu
rodzic
commit
70e04078fb

+ 13 - 0
Trio Watch App Extension/Helper/Helper+Enums.swift

@@ -19,6 +19,19 @@ enum AcknowledgementStatus: String, CaseIterable {
     case pending
 }
 
+enum AcknowledgmentCode: String, Codable {
+    case savingCarbs = "saving_carbs"
+    case enactingBolus = "enacting_bolus"
+    case comboComplete = "combo_complete"
+    case carbsLogged = "carbs_logged"
+    case overrideStarted = "override_started"
+    case overrideStopped = "override_stopped"
+    case tempTargetStarted = "temp_target_started"
+    case tempTargetStopped = "temp_target_stopped"
+    case genericSuccess = "success"
+    case genericFailure = "failure"
+}
+
 enum WatchSize {
     case watch40mm
     case watch41mm

+ 1 - 27
Trio Watch App Extension/WatchState+Requests.swift

@@ -13,10 +13,7 @@ extension WatchState {
             return
         }
 
-        isBolusCanceled = false // Reset canceled state when starting new bolus
-
         WatchLogger.shared.log("⌚️ Sending bolus request: \(amount)U")
-        WatchLogger.shared.log("⌚️ isBolusCanceled = false")
 
         let message: [String: Any] = [
             WatchMessageKeys.bolus: amount
@@ -53,6 +50,7 @@ extension WatchState {
         }
 
         // Display pending communication animation
+        showCommsAnimation = true
         WatchLogger.shared.log("⌚️ showCommsAnimation = true")
     }
 
@@ -146,30 +144,6 @@ extension WatchState {
         WatchLogger.shared.log("⌚️ showCommsAnimation = true")
     }
 
-    /// Sends a request to cancel the current bolus delivery to the paired iPhone
-    func sendCancelBolusRequest() {
-        isBolusCanceled = true
-
-        guard let session = session, session.isReachable else {
-            WatchLogger.shared.log("⌚️ Cancel bolus request aborted: session unreachable")
-            return
-        }
-
-        WatchLogger.shared.log("⌚️ Sending cancel bolus request. bolusCanceled = true")
-
-        let message: [String: Any] = [
-            WatchMessageKeys.cancelBolus: true
-        ]
-
-        session.sendMessage(message, replyHandler: nil) { error in
-            WatchLogger.shared.log("Error sending cancel bolus request: \(error.localizedDescription)")
-        }
-
-        // Display pending communication animation
-        showCommsAnimation = true
-        WatchLogger.shared.log("⌚️ showCommsAnimation = true")
-    }
-
     /// Sends a request to calculate a bolus recommendation based on the current carbs amount
     func requestBolusRecommendation() {
         guard let session = session, session.isReachable else {

+ 10 - 12
Trio Watch App Extension/WatchState.swift

@@ -36,11 +36,6 @@ import WatchConnectivity
     var bolusAmount: Double = 0.0
     var confirmationProgress: Double = 0.0
 
-//    var bolusProgress: Double = 0.0
-//    var activeBolusAmount: Double = 0.0
-//    var deliveredAmount: Double = 0.0
-    var isBolusCanceled = false
-
     // Safety limits
     var maxBolus: Decimal = 10
     var maxCarbs: Decimal = 250
@@ -186,9 +181,10 @@ import WatchConnectivity
         // e.g. { "acknowledged": true, "message": "Started Temp Target...", "date": Date(...) }
         else if
             let acknowledged = message[WatchMessageKeys.acknowledged] as? Bool,
-            let ackMessage = message[WatchMessageKeys.message] as? String
+            let ackMessage = message[WatchMessageKeys.message] as? String,
+            let ackCodeRaw = message[WatchMessageKeys.ackCode] as? String
         {
-            WatchLogger.shared.log("⌚️ Handling ack with message: \(ackMessage), success: \(acknowledged)")
+            WatchLogger.shared.log("⌚️ Handling ack with message: \(ackMessage), success: \(acknowledged), ackCode: \(ackCodeRaw)")
             DispatchQueue.main.async {
                 // For ack messages, we do NOT show “Syncing...”
                 self.showSyncingAnimation = false
@@ -298,7 +294,9 @@ import WatchConnectivity
         DispatchQueue.main.async {
             // 1) Acknowledgment logic
             if let acknowledged = message[WatchMessageKeys.acknowledged] as? Bool,
-               let ackMessage = message[WatchMessageKeys.message] as? String
+               let ackMessage = message[WatchMessageKeys.message] as? String,
+               let ackCodeRaw = message[WatchMessageKeys.ackCode] as? String,
+               let ackCode = AcknowledgmentCode(rawValue: ackCodeRaw)
             {
                 DispatchQueue.main.async {
                     self.showSyncingAnimation = false
@@ -306,18 +304,18 @@ import WatchConnectivity
 
                 WatchLogger.shared.log("⌚️ Received acknowledgment: \(ackMessage), success: \(acknowledged)")
 
-                switch ackMessage {
-                case "Saving carbs...":
+                switch ackCode {
+                case .savingCarbs:
                     self.isMealBolusCombo = true
                     self.mealBolusStep = .savingCarbs
                     self.showCommsAnimation = true
                     self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: false)
-                case "Enacting bolus...":
+                case .enactingBolus:
                     self.isMealBolusCombo = true
                     self.mealBolusStep = .enactingBolus
                     self.showCommsAnimation = true
                     self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: false)
-                case "Carbs and bolus logged successfully":
+                case .comboComplete:
                     self.isMealBolusCombo = false
                     self.handleAcknowledgment(success: acknowledged, message: ackMessage, isFinal: true)
                 default:

+ 2 - 3
Trio/Sources/APS/Storage/OverrideStorage.swift

@@ -394,13 +394,12 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
         )
 
         return try await context.perform {
-            guard let fetchedResults = results as? [OverrideStored],
-                  let latestOverride = fetchedResults.first
+            guard let fetchedResults = results as? [OverrideStored]
             else {
                 throw CoreDataError.fetchError(function: #function, file: #file)
             }
 
-            return latestOverride.objectID
+            return fetchedResults.first?.objectID
         }
     }
 }

+ 2 - 1
Trio/Sources/Models/WatchMessageKeys.swift

@@ -1,11 +1,12 @@
 enum WatchMessageKeys {
     // Request/Response Keys
     static let date = "date"
+    static let units = "units"
     static let requestWatchUpdate = "requestWatchUpdate"
     static let watchState = "watchState"
     static let acknowledged = "acknowledged"
+    static let ackCode = "ackCode"
     static let message = "message"
-    static let units = "units"
 
     // Treatment Keys
     static let bolus = "bolus"

+ 133 - 80
Trio/Sources/Services/WatchManager/AppleWatchManager.swift

@@ -481,7 +481,7 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
         }
     }
 
-    func sendAcknowledgment(toWatch success: Bool, message: String = "") {
+    func sendAcknowledgment(toWatch success: Bool, message: String = "", ackCode: AcknowledgmentCode) {
         guard let session = session, session.isReachable else {
             debug(.watchManager, "⌚️ Watch not reachable for acknowledgment")
             return
@@ -489,7 +489,8 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
 
         let ackMessage: [String: Any] = [
             WatchMessageKeys.acknowledged: success,
-            WatchMessageKeys.message: message
+            WatchMessageKeys.message: message,
+            WatchMessageKeys.ackCode: ackCode.rawValue
         ]
 
         session.sendMessage(ackMessage, replyHandler: nil) { error in
@@ -581,20 +582,6 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                 self?.handleCancelTempTarget()
             }
 
-            // Handle bolus cancellation
-            if message[WatchMessageKeys.cancelBolus] as? Bool == true {
-                Task {
-                    await self?.apsManager.cancelBolus { [self] success, message in
-                        // Acknowledge success or error of bolus
-                        self?.sendAcknowledgment(toWatch: success, message: message)
-                    }
-                    debug(.watchManager, "📱 Bolus cancelled from watch")
-
-                    // perform determine basal sync, otherwise you could end up with too much IOB when opening the calculator again
-                    try await self?.apsManager.determineBasalSync()
-                }
-            }
-
             if message[WatchMessageKeys.requestBolusRecommendation] as? Bool == true {
                 let carbs = message[WatchMessageKeys.carbs] as? Int ?? 0
 
@@ -660,7 +647,11 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
         Task {
             await apsManager.enactBolus(amount: Double(amount), isSMB: false) { success, message in
                 // Acknowledge success or error of bolus
-                self.sendAcknowledgment(toWatch: success, message: message)
+                self.sendAcknowledgment(
+                    toWatch: success,
+                    message: message,
+                    ackCode: success == true ? .genericSuccess : .genericFailure
+                )
             }
             debug(.watchManager, "📱 Enacted bolus via APS Manager: \(amount)U")
         }
@@ -694,13 +685,14 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                         message: String(
                             localized: "Carbs logged successfully.",
                             comment: "Success message sent to watch when carbs are logged successfully"
-                        )
+                        ),
+                        ackCode: .carbsLogged
                     )
                 } catch {
                     debug(.watchManager, "❌ Error saving carbs: \(error.localizedDescription)")
 
                     // Acknowledge failure
-                    self.sendAcknowledgment(toWatch: false, message: "Error logging carbs")
+                    self.sendAcknowledgment(toWatch: false, message: "Error logging carbs", ackCode: .genericFailure)
                 }
             }
         }
@@ -719,7 +711,8 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                 // Notify Watch: "Saving carbs..."
                 self.sendAcknowledgment(
                     toWatch: true,
-                    message: String(localized: "Saving Carbs...", comment: "Successful message sent to watch when saving carbs")
+                    message: String(localized: "Saving Carbs...", comment: "Successful message sent to watch when saving carbs"),
+                    ackCode: .savingCarbs
                 )
 
                 // Save carbs entry in Core Data
@@ -743,14 +736,19 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                     message: String(
                         localized: "Enacting bolus...",
                         comment: "Successful message sent to watch when enacting bolus"
-                    )
+                    ),
+                    ackCode: .enactingBolus
                 )
 
                 // Enact bolus via APS Manager
                 let bolusDouble = NSDecimalNumber(decimal: bolusAmount).doubleValue
                 await apsManager.enactBolus(amount: bolusDouble, isSMB: false) { success, message in
                     // Acknowledge success or error of bolus
-                    self.sendAcknowledgment(toWatch: success, message: message)
+                    self.sendAcknowledgment(
+                        toWatch: success,
+                        message: message,
+                        ackCode: success == true ? .genericSuccess : .genericFailure
+                    )
                 }
                 debug(.watchManager, "📱 Enacted bolus from watch via APS Manager: \(bolusDouble) U")
                 // Notify Watch: "Carbs and bolus logged successfully"
@@ -759,12 +757,13 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                     message: String(
                         localized: "Carbs and Bolus logged successfully.",
                         comment: "Successful message sent to watch when logging carbs and bolus"
-                    )
+                    ),
+                    ackCode: .comboComplete
                 )
 
             } catch {
                 debug(.watchManager, "❌ Error processing combined request: \(error.localizedDescription)")
-                sendAcknowledgment(toWatch: false, message: "Failed to log carbs and bolus")
+                sendAcknowledgment(toWatch: false, message: "Failed to log carbs and bolus", ackCode: .genericFailure)
             }
         }
     }
@@ -794,14 +793,26 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                             )
 
                             // Acknowledge cancellation success
-                            self.sendAcknowledgment(toWatch: true, message: "Stopped Override successfully.")
+                            self.sendAcknowledgment(
+                                toWatch: true,
+                                message: "Stopped Override successfully.",
+                                ackCode: .overrideStopped
+                            )
                         } catch {
                             debug(.watchManager, "❌ Error cancelling override: \(error.localizedDescription)")
                             // Acknowledge cancellation error
-                            self.sendAcknowledgment(toWatch: false, message: "Error stopping Override.")
+                            self.sendAcknowledgment(toWatch: false, message: "Error stopping Override.", ackCode: .genericFailure)
                         }
                     }
                 }
+            } else {
+                debug(.watchManager, "❌ No active override found.")
+                self.sendAcknowledgment(
+                    toWatch: false,
+                    message: "No active override found.",
+                    ackCode: .genericFailure
+                )
+                return
             }
         }
     }
@@ -810,49 +821,83 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
         Task {
             let context = CoreDataStack.shared.newTaskContext()
 
+            debug(.watchManager, "📱 Fetching all override presets...")
+
             // Fetch all presets to find the one to activate
             let presetIds = try await overrideStorage.fetchForOverridePresets()
             let presets: [OverrideStored] = try await CoreDataStack.shared
                 .getNSManagedObject(with: presetIds, context: context)
 
-            // Check for active override
-            if let activeOverrideId = try await overrideStorage.fetchLatestActiveOverride() {
-                let activeOverride = await context.perform {
-                    context.object(with: activeOverrideId) as? OverrideStored
-                }
+            debug(.watchManager, "📱 Checking for active override...")
 
-                // Deactivate if exists
-                if let override = activeOverride {
-                    await context.perform {
-                        override.enabled = false
+            do {
+                // Check for active override
+                if let activeOverrideId = try await overrideStorage.fetchLatestActiveOverride() {
+                    let activeOverride = await context.perform {
+                        context.object(with: activeOverrideId) as? OverrideStored
+                    }
+
+                    // Deactivate, if necessary
+                    if let override = activeOverride {
+                        await context.perform {
+                            override.enabled = false
+                        }
                     }
+                } else {
+                    debug(.watchManager, "📱 Currently no override is active... proceeding to activate override: \(presetName)")
                 }
+            } catch {
+                debug(.watchManager, "❌ Error while checking for active override: \(error.localizedDescription)")
+                self.sendAcknowledgment(
+                    toWatch: false,
+                    message: "Failed to load active override.",
+                    ackCode: .genericFailure
+                )
+                return
             }
 
             // Activate the selected preset
             await context.perform {
-                if let presetToActivate = presets.first(where: { $0.name == presetName }) {
-                    presetToActivate.enabled = true
-                    presetToActivate.date = Date()
+                guard let presetToActivate = presets
+                    .first(where: { $0.name?.trimmingCharacters(in: .whitespacesAndNewlines) == presetName })
+                else {
+                    debug(.watchManager, "❌ No matching preset found for name: \"\(presetName)\" in \(presets.map(\.name))")
+                    self.sendAcknowledgment(
+                        toWatch: false,
+                        message: "Preset not found: \(presetName)",
+                        ackCode: .genericFailure
+                    )
+                    return
+                }
 
-                    do {
-                        guard context.hasChanges else { return }
-                        try context.save()
-                        debug(.watchManager, "📱 Successfully activated override: \(presetName)")
+                presetToActivate.enabled = true
+                presetToActivate.date = Date()
 
-                        // Send notification to update Adjustments UI
-                        Foundation.NotificationCenter.default.post(
-                            name: .didUpdateOverrideConfiguration,
-                            object: nil
-                        )
+                do {
+                    guard context.hasChanges else { return }
+                    try context.save()
+                    debug(.watchManager, "📱 Successfully activated override: \(presetName)")
 
-                        // Acknowledge activation success
-                        self.sendAcknowledgment(toWatch: true, message: "Started Override \"\(presetName)\" successfully.")
-                    } catch {
-                        debug(.watchManager, "❌ Error activating override: \(error.localizedDescription)")
-                        // Acknowledge activation error
-                        self.sendAcknowledgment(toWatch: false, message: "Error activating Override \"\(presetName)\".")
-                    }
+                    // Send notification to update Adjustments UI
+                    Foundation.NotificationCenter.default.post(
+                        name: .didUpdateOverrideConfiguration,
+                        object: nil
+                    )
+
+                    // Acknowledge activation success
+                    self.sendAcknowledgment(
+                        toWatch: true,
+                        message: "Started Override \"\(presetName)\" successfully.",
+                        ackCode: .overrideStarted
+                    )
+                } catch {
+                    debug(.watchManager, "❌ Error activating override: \(error.localizedDescription)")
+                    // Acknowledge activation error
+                    self.sendAcknowledgment(
+                        toWatch: false,
+                        message: "Error activating Override \"\(presetName)\".",
+                        ackCode: .genericFailure
+                    )
                 }
             }
         }
@@ -920,11 +965,19 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                         )
 
                         // Acknowledge activation success
-                        self.sendAcknowledgment(toWatch: true, message: "Started Temp Target \"\(presetName)\" successfully.")
+                        self.sendAcknowledgment(
+                            toWatch: true,
+                            message: "Started Temp Target \"\(presetName)\" successfully.",
+                            ackCode: .tempTargetStarted
+                        )
                     } catch {
                         debug(.watchManager, "❌ Error activating temp target: \(error.localizedDescription)")
                         // Acknowledge activation error
-                        self.sendAcknowledgment(toWatch: false, message: "Error activating Temp Target \"\(presetName)\".")
+                        self.sendAcknowledgment(
+                            toWatch: false,
+                            message: "Error activating Temp Target \"\(presetName)\".",
+                            ackCode: .genericFailure
+                        )
                     }
                 }
             }
@@ -959,11 +1012,19 @@ final class BaseWatchManager: NSObject, WCSessionDelegate, Injectable, WatchMana
                             )
 
                             // Acknowledge cancellation success
-                            self.sendAcknowledgment(toWatch: true, message: "Stopped Temp Target successfully.")
+                            self.sendAcknowledgment(
+                                toWatch: true,
+                                message: "Stopped Temp Target successfully.",
+                                ackCode: .tempTargetStopped
+                            )
                         } catch {
                             debug(.watchManager, "❌ Error stopping temp target: \(error.localizedDescription)")
                             // Acknowledge cancellation error
-                            self.sendAcknowledgment(toWatch: false, message: "Error stopping Temp Target.")
+                            self.sendAcknowledgment(
+                                toWatch: false,
+                                message: "Error stopping Temp Target.",
+                                ackCode: .genericFailure
+                            )
                         }
                     }
                 }
@@ -1045,25 +1106,17 @@ extension BaseWatchManager {
     }
 }
 
-// extension BaseWatchManager {
-//    func pushWatchStateToWatch(_ state: WatchState) {
-//        guard let session = session else { return }
-//
-//        // Skip if we already sent this state or older
-//        let lastSent = WatchStateSnapshot.loadLatestDateFromDisk()
-//        guard lastSent < state.date else {
-//            debug(.watchManager, "🕐 Skipping push — newer or equal state already sent")
-//            return
-//        }
-//
-//        let message: [String: Any] = [
-//            WatchMessageKeys.date: state.date.timeIntervalSince1970,
-//            WatchMessageKeys.watchState: watchStateToDictionary(from: state)
-//        ]
-//
-//        WatchStateSnapshot.saveLatestDateToDisk(state.date)
-//        session.transferUserInfo(message)
-//
-//        debug(.watchManager, "📤 Transferred new WatchState snapshot via userInfo")
-//    }
-// }
+extension BaseWatchManager {
+    enum AcknowledgmentCode: String, Codable {
+        case savingCarbs = "saving_carbs"
+        case enactingBolus = "enacting_bolus"
+        case comboComplete = "combo_complete"
+        case carbsLogged = "carbs_logged"
+        case overrideStarted = "override_started"
+        case overrideStopped = "override_stopped"
+        case tempTargetStarted = "temp_target_started"
+        case tempTargetStopped = "temp_target_stopped"
+        case genericSuccess = "success"
+        case genericFailure = "failure"
+    }
+}