Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions LoopFollow/Helpers/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import LocalAuthentication
public enum AuthResult {
case success
case canceled
case unavailable
case unavailable(String)
case failed
}

Expand All @@ -29,7 +29,20 @@ public enum AuthService {

var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
completion(.unavailable)
var message = "Device authentication is not available. "

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))
}
return
}

Expand Down
30 changes: 16 additions & 14 deletions LoopFollow/Remote/LoopAPNS/LoopAPNSBolusView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
}
Expand Down
18 changes: 16 additions & 2 deletions LoopFollow/Remote/TRC/BolusView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
}),
Expand Down
20 changes: 17 additions & 3 deletions LoopFollow/Remote/TRC/MealView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}),
Expand Down