taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit aeab8aa5d8200e5605cb45cb8d9e7aa890ff242b
parent 1df0c238686c127613b2658a1791e282cffd58fe
Author: Marc Stibane <marc@taler.net>
Date:   Thu,  5 Jun 2025 16:14:15 +0200

BiometricService

Diffstat:
ATalerWallet1/Controllers/BiometricService.swift | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+), 0 deletions(-)

diff --git a/TalerWallet1/Controllers/BiometricService.swift b/TalerWallet1/Controllers/BiometricService.swift @@ -0,0 +1,75 @@ +/* + * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. + * See LICENSE.md + */ +/** + * + * @author Marc Stibane + */ +import SwiftUI +import LocalAuthentication + +class BiometricService: ObservableObject { + static let shared = BiometricService() + @Published var isAuthenticated = false + @Published var authenticationError: String? + + private var context: LAContext? + + func authenticateUser() { + let reason = resetContext() + + guard let context = context else { + authenticationError = String(localized: "Failed to initialize authentication context.", comment: "FaceID") + return + } + guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) else { + authenticationError = String(localized: "Authentication is not available on this device.", comment: "FaceID") + return + } + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { [weak self] success, authenticationError in + DispatchQueue.main.async { + if success { + self?.isAuthenticated = true + self?.authenticationError = nil + } else if let error = authenticationError as? LAError { + self?.authenticationError = self?.errorMessage(for: error) + } else { + self?.authenticationError = String(localized: "Unknown authentication error occurred.", comment: "FaceID") + } + } + } + } + + private func resetContext() -> String { + let reason = String(localized: "Authenticate to access your money", comment: "FaceID") + context = LAContext() + context?.localizedReason = reason + context?.localizedCancelTitle = String(localized: "Cancel", comment: "FaceID") + context?.localizedFallbackTitle = String(localized: "Use Passcode", comment: "FaceID") + return reason + } + + private func errorMessage(for error: LAError) -> String { + switch error.code { + case .authenticationFailed: + return String(localized: "Authentication failed. Please try again.", comment: "FaceID") + case .userCancel: + return String(localized: "You canceled the authentication.", comment: "FaceID") + case .userFallback: + return String(localized: "You chose to use the fallback option.", comment: "FaceID") + case .systemCancel: + return String(localized: "The system canceled the authentication.", comment: "FaceID") + case .passcodeNotSet: + return String(localized: "Passcode is not set on this device.", comment: "FaceID") + case .biometryNotAvailable: + return String(localized: "Biometric authentication is not available on this device.", comment: "FaceID") + case .biometryNotEnrolled: + return String(localized: "No biometrics are enrolled on this device.", comment: "FaceID") + case .biometryLockout: + return String(localized: "Biometric authentication is locked. Enter your passcode to unlock.", comment: "FaceID") + default: + return String(localized: "An unknown error occurred.", comment: "FaceID") + } + } +}