// ── Biometric Authentication ──────────────────────────────── // Generic Face ID / Touch ID wrapper using LocalAuthentication. // Product apps pass a custom reason string for the biometric prompt. // Not available on watchOS — guarded with #if canImport. import Foundation #if canImport(LocalAuthentication) import LocalAuthentication /// Generic biometric authentication for all ByteLyst iOS/macOS apps. /// Not available on watchOS (LocalAuthentication is iOS/macOS only). public enum BLBiometricAuth { public enum BiometricType { case faceID, touchID, none } /// Check what biometric type is available on this device. public static var availableType: BiometricType { let context = LAContext() var error: NSError? guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { return .none } switch context.biometryType { case .faceID: return .faceID case .touchID: return .touchID default: return .none } } /// Whether biometric auth is available on this device. public static var isAvailable: Bool { availableType != .none } /// Whether the user has enabled biometric lock in settings. /// Uses a configurable UserDefaults key. public static func isEnabled(key: String = "biometric_lock_enabled") -> Bool { UserDefaults.standard.bool(forKey: key) } /// Set biometric lock enabled state. public static func setEnabled(_ enabled: Bool, key: String = "biometric_lock_enabled") { UserDefaults.standard.set(enabled, forKey: key) } /// Authenticate with biometrics only. Returns true on success. public static func authenticate(reason: String = "Unlock app") async -> Bool { let context = LAContext() context.localizedCancelTitle = "Use Password" do { return try await context.evaluatePolicy( .deviceOwnerAuthenticationWithBiometrics, localizedReason: reason ) } catch { return false } } /// Authenticate with biometrics or device passcode fallback. public static func authenticateWithPasscode(reason: String = "Unlock app") async -> Bool { let context = LAContext() do { return try await context.evaluatePolicy( .deviceOwnerAuthentication, localizedReason: reason ) } catch { return false } } } #endif