BiometricService.swift (3670B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * 7 * @author Marc Stibane 8 */ 9 import SwiftUI 10 import LocalAuthentication 11 12 class BiometricService: ObservableObject { 13 static let shared = BiometricService() 14 @Published var canAuthenticate: Bool = true 15 @Published var isAuthenticated = false 16 @Published var authenticationError: String? 17 18 @AppStorage("useAuthentication") var useAuthentication: Bool = false 19 20 private var context: LAContext? 21 22 func authenticateUser() { 23 let reason = resetContext() 24 25 if useAuthentication { 26 guard let context = context else { 27 authenticationError = String(localized: "Failed to initialize authentication context.", comment: "FaceID") 28 canAuthenticate = false 29 return 30 } 31 guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else { 32 authenticationError = String(localized: "Authentication is not available on this device.", comment: "FaceID") 33 canAuthenticate = false 34 return 35 } 36 canAuthenticate = true 37 context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { [weak self] success, authenticationError in 38 DispatchQueue.main.async { 39 if success { 40 self?.isAuthenticated = true 41 self?.authenticationError = nil 42 } else if let error = authenticationError as? LAError { 43 self?.authenticationError = self?.errorMessage(for: error) 44 } else { 45 self?.authenticationError = String(localized: "Unknown authentication error occurred.", comment: "FaceID") 46 } 47 } 48 } 49 } 50 } 51 52 private func resetContext() -> String { 53 let reason = String(localized: "Authenticate to access your money", comment: "FaceID") 54 context = LAContext() 55 context?.localizedReason = reason 56 context?.localizedCancelTitle = String(localized: "Cancel", comment: "FaceID") 57 context?.localizedFallbackTitle = String(localized: "Use Passcode", comment: "FaceID") 58 return reason 59 } 60 61 private func errorMessage(for error: LAError) -> String { 62 switch error.code { 63 case .authenticationFailed: 64 return String(localized: "Authentication failed. Please try again.", comment: "FaceID") 65 case .userCancel: 66 return String(localized: "You canceled the authentication.", comment: "FaceID") 67 case .userFallback: 68 return String(localized: "You chose to use the fallback option.", comment: "FaceID") 69 case .systemCancel: 70 return String(localized: "The system canceled the authentication.", comment: "FaceID") 71 case .passcodeNotSet: 72 return String(localized: "Passcode is not set on this device.", comment: "FaceID") 73 case .biometryNotAvailable: 74 return String(localized: "Biometric authentication is not available on this device.", comment: "FaceID") 75 case .biometryNotEnrolled: 76 return String(localized: "No biometrics are enrolled on this device.", comment: "FaceID") 77 case .biometryLockout: 78 return String(localized: "Biometric authentication is locked. Enter your passcode to unlock.", comment: "FaceID") 79 default: 80 return String(localized: "An unknown error occurred.", comment: "FaceID") 81 } 82 } 83 }