From c6e91cd24b39c4a041b8eb849af8467446059001 Mon Sep 17 00:00:00 2001 From: codebymini Date: Thu, 12 Feb 2026 22:03:15 +0100 Subject: [PATCH 1/2] Add error message when no passcode has been set up --- LoopFollow/Helpers/AuthService.swift | 22 ++++++++++++-- .../Remote/LoopAPNS/LoopAPNSBolusView.swift | 30 ++++++++++--------- LoopFollow/Remote/TRC/BolusView.swift | 18 +++++++++-- LoopFollow/Remote/TRC/MealView.swift | 20 +++++++++++-- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/LoopFollow/Helpers/AuthService.swift b/LoopFollow/Helpers/AuthService.swift index c00b1db9b..a62fab1f2 100644 --- a/LoopFollow/Helpers/AuthService.swift +++ b/LoopFollow/Helpers/AuthService.swift @@ -7,7 +7,7 @@ import LocalAuthentication public enum AuthResult { case success case canceled - case unavailable + case unavailable(String) case failed } @@ -29,7 +29,25 @@ public enum AuthService { var error: NSError? guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { - completion(.unavailable) + var message = "Device authentication is not available. " + + if #available(iOS 11, *) { + let biometryType = context.biometryType + if biometryType == .none { + message += "Please enable Face ID, Touch ID, or set up a device passcode in Settings." + } else if biometryType == .faceID { + message += "Face ID is not available. Please set up a device passcode in Settings." + } else if biometryType == .touchID { + message += "Touch ID is not available. Please set up a device passcode in Settings." + } + } else { + message += "Please enable Touch ID or set up a device passcode in Settings." + } + + + DispatchQueue.main.async { + completion(.unavailable(message)) + } return } diff --git a/LoopFollow/Remote/LoopAPNS/LoopAPNSBolusView.swift b/LoopFollow/Remote/LoopAPNS/LoopAPNSBolusView.swift index d3ae79c9e..011fe0e10 100644 --- a/LoopFollow/Remote/LoopAPNS/LoopAPNSBolusView.swift +++ b/LoopFollow/Remote/LoopAPNS/LoopAPNSBolusView.swift @@ -326,20 +326,22 @@ struct LoopAPNSBolusView: View { private func authenticateAndSendInsulin() { AuthService.authenticate(reason: "Confirm your identity to send insulin.") { result in - switch result { - case .success: - sendInsulinConfirmed() - case .unavailable: - alertMessage = "Authentication not available" - alertType = .error - showAlert = true - case .failed: - alertMessage = "Authentication failed" - alertType = .error - showAlert = true - case .canceled: - // User canceled: no alert to avoid spammy UX - break + DispatchQueue.main.async { + switch result { + case .success: + self.sendInsulinConfirmed() + case let .unavailable(message): + self.alertMessage = message + self.alertType = .error + self.showAlert = true + case .failed: + self.alertMessage = "Authentication failed" + self.alertType = .error + self.showAlert = true + case .canceled: + // User canceled: no alert to avoid spammy UX + break + } } } } diff --git a/LoopFollow/Remote/TRC/BolusView.swift b/LoopFollow/Remote/TRC/BolusView.swift index 0a70f86b9..30bfab213 100644 --- a/LoopFollow/Remote/TRC/BolusView.swift +++ b/LoopFollow/Remote/TRC/BolusView.swift @@ -113,8 +113,22 @@ struct BolusView: View { message: Text("Are you sure you want to send \(InsulinFormatter.shared.string(bolusAmount)) U?"), primaryButton: .default(Text("Confirm"), action: { AuthService.authenticate(reason: "Confirm your identity to send bolus.") { result in - if case .success = result { - sendBolus() + DispatchQueue.main.async { + switch result { + case .success: + self.sendBolus() + case let .unavailable(message): + self.alertMessage = message + self.alertType = .validation + self.showAlert = true + case .failed: + self.alertMessage = "Authentication failed" + self.alertType = .validation + self.showAlert = true + case .canceled: + // User canceled, no alert + break + } } } }), diff --git a/LoopFollow/Remote/TRC/MealView.swift b/LoopFollow/Remote/TRC/MealView.swift index 517db1811..4c5e27df6 100644 --- a/LoopFollow/Remote/TRC/MealView.swift +++ b/LoopFollow/Remote/TRC/MealView.swift @@ -196,12 +196,26 @@ struct MealView: View { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { if bolusAmount > 0 { AuthService.authenticate(reason: "Confirm your identity to send bolus.") { result in - if case .success = result { - sendMealCommand() + DispatchQueue.main.async { + switch result { + case .success: + self.sendMealCommand() + case let .unavailable(message): + self.alertMessage = message + self.alertType = .validationError + self.showAlert = true + case .failed: + self.alertMessage = "Authentication failed" + self.alertType = .validationError + self.showAlert = true + case .canceled: + // User canceled, no alert + break + } } } } else { - sendMealCommand() + self.sendMealCommand() } } }), From 945d30affac5472c580221e186f820d8125b41a5 Mon Sep 17 00:00:00 2001 From: codebymini Date: Fri, 13 Feb 2026 10:11:47 +0100 Subject: [PATCH 2/2] Refactor authentication error message handling since target is higher than ios 16.6 --- LoopFollow/Helpers/AuthService.swift | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/LoopFollow/Helpers/AuthService.swift b/LoopFollow/Helpers/AuthService.swift index a62fab1f2..d065054c8 100644 --- a/LoopFollow/Helpers/AuthService.swift +++ b/LoopFollow/Helpers/AuthService.swift @@ -31,20 +31,15 @@ public enum AuthService { guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { var message = "Device authentication is not available. " - if #available(iOS 11, *) { - let biometryType = context.biometryType - if biometryType == .none { - message += "Please enable Face ID, Touch ID, or set up a device passcode in Settings." - } else if biometryType == .faceID { - message += "Face ID is not available. Please set up a device passcode in Settings." - } else if biometryType == .touchID { - message += "Touch ID is not available. Please set up a device passcode in Settings." - } - } else { - message += "Please enable Touch ID or set up a device passcode in Settings." + let biometryType = context.biometryType + if biometryType == .none { + message += "Please enable Face ID, Touch ID, or set up a device passcode in Settings." + } else if biometryType == .faceID { + message += "Face ID is not available. Please set up a device passcode in Settings." + } else if biometryType == .touchID { + message += "Touch ID is not available. Please set up a device passcode in Settings." } - DispatchQueue.main.async { completion(.unavailable(message)) }