Last active
November 13, 2023 16:53
-
-
Save gaussbeam/666484767c5c09c551065e278cbe3900 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import LocalAuthentication | |
class BiometricAuthenticationManager { | |
enum BiometricAuthenticationType { | |
case faceID(permitted: Bool) | |
case touchID | |
case none | |
} | |
enum AuthenticationResult { | |
case success | |
case failure(LAError?) | |
} | |
var context: LAContext? | |
public static let shared = BiometricAuthenticationManager() | |
fileprivate init() {} | |
} | |
// MARK: - Interface | |
extension BiometricAuthenticationManager { | |
var biometricAuthenticationType: BiometricAuthenticationType { | |
let context = LAContext() | |
var error: NSError? | |
// Check if biometric authentication is available. | |
// If it is disabled to access FaceID, `canEvaluatePolicy()` returns `false` and `LAError.biometryNotAvailable` is assigned to error. | |
let isEvaluateSuccess = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) | |
if let e = error as? LAError { | |
// Handle error if needed. | |
} | |
let type: BiometricAuthenticationType | |
if #available(iOS 11.0, *) { | |
// In iOS11 or later, determine type by `LABiometryType` | |
switch context.biometryType { | |
case .faceID: | |
type = .faceID(permitted: isEvaluateSuccess) | |
case .touchID: | |
type = .touchID | |
case .none: | |
type = .none | |
} | |
} else { | |
if isEvaluateSuccess { | |
type = .touchID | |
} else { | |
type = .none | |
} | |
} | |
return type | |
} | |
/// - Returns: Whether if user allows app to use biometric authentication. | |
var permitBiometricAuthentication: Bool { | |
switch self.biometricAuthenticationType { | |
case .faceID(let permitted): | |
return permitted | |
case .touchID: | |
return true | |
case .none: | |
return false | |
} | |
} | |
/// - Returns: Whether if device supports biometric authentication. | |
var supportsBiometricAuthentication: Bool { | |
switch self.biometricAuthenticationType { | |
case .faceID, .touchID: | |
return true | |
case .none: | |
return false | |
} | |
} | |
func authenticate(reason: String, _ completionHandler: @escaping ((AuthenticationResult) -> Void)) { | |
// If authentication was passed once, instance of LAContext always returns true. | |
// To avoid this, create instance of it for every time. | |
self.context = LAContext() | |
context?.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] (success, error) in | |
defer { | |
self?.context = nil | |
} | |
guard success else { | |
let e = error as? LAError | |
completionHandler(.failure(e)) | |
return | |
} | |
completionHandler(.success) | |
} | |
} | |
func cancelAuthentication() { | |
self.context?.invalidate() | |
self.context = nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment