// // ClientParameterBiometricStatusHandler.swift // MobileFirstFramework // // Created by Scott Pfeil on 4/11/23. // Copyright © 2023 Verizon Wireless. All rights reserved. // import MVMCore import LocalAuthentication public final class ClientParameterBiometricStatusHandler: ClientParameterProtocol { public static var name: String = ClientParameterBiometricStatusModel.identifier public var clientParameterModel: ClientParameterBiometricStatusModel required public init(clientParameterModel: ClientParameterBiometricStatusModel) { self.clientParameterModel = clientParameterModel } public func fetchClientParameters(requestParameters: [String : Any], timingOutIn timeout: TimeInterval) async -> [String : AnyHashable] { var error: NSError? let context = LAContext() guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else { guard let error = error, let errorType = LAError.Code(rawValue: error.code)?.rawValueString else { return formatReturnParameters("unknown") } return formatReturnParameters(errorType) } return formatReturnParameters("enrolled").mergingRight(["biometricType": context.biometryType.rawValueString]) } } extension LAError.Code: RawRepresentable { public init?(rawValue: String) { switch rawValue { case "authenticationFailed": self = .authenticationFailed case "userCancel": self = .userCancel case "userFallback": self = .userFallback case "systemCancel": self = .systemCancel case "passcodeNotSet": self = .passcodeNotSet case "appCancel": self = .appCancel case "invalidContext": self = .invalidContext case "biometryNotAvailable": self = .biometryNotAvailable case "biometryNotEnrolled": self = .biometryNotEnrolled case "biometryLockout": self = .biometryLockout case "notInteractive": self = .notInteractive default: return nil } } var rawValueString: String { switch self { case .authenticationFailed: return "authenticationFailed" case .userCancel: return "userCancel" case .userFallback: return "userFallback" case .systemCancel: return "systemCancel" case .passcodeNotSet: return "passcodeNotSet" case .appCancel: return "appCancel" case .invalidContext: return "invalidContext" case .biometryNotAvailable: return "biometryNotAvailable" case .biometryNotEnrolled: return "biometryNotEnrolled" case .biometryLockout: return "biometryLockout" case .notInteractive: return "notInteractive" case .touchIDNotAvailable: return "biometryNotAvailable" case .touchIDNotEnrolled: return "biometryNotEnrolled" case .touchIDLockout: return "biometryLockout" @unknown default: return "unknown" } } } extension LABiometryType: RawRepresentable { public init?(rawValue: String) { switch rawValue { case "faceID": self = .faceID case "touchID": self = .touchID case "none": self = .none default: return nil } } var rawValueString: String { switch self { case .faceID: return "faceID" case .touchID: return "touchID" case .none: return "none" case .opticID: // TODO: !!!! return "" @unknown default: return "unknown" } } }