Skip to content

Instantly share code, notes, and snippets.

@gaussbeam
Last active November 13, 2023 16:53
Show Gist options
  • Save gaussbeam/666484767c5c09c551065e278cbe3900 to your computer and use it in GitHub Desktop.
Save gaussbeam/666484767c5c09c551065e278cbe3900 to your computer and use it in GitHub Desktop.
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