Last active
July 6, 2020 16:11
-
-
Save maximkrouk/9e5839b9c3eba6dad076d8f235fa2c04 to your computer and use it in GitHub Desktop.
Property wrapper for system FaceID/TouchID/Password secured actions
This file contains 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 Foundation | |
import LocalAuthentication | |
@propertyWrapper | |
public class Authenticated { | |
public typealias Action = () -> Void | |
private var _action: Action = {} | |
private var _onStart: Action? | |
private var _onSuccess: Action? | |
private var _onFailure: ((LAContext.EvaluationFailure) -> Void)? | |
private var _onCompletion: Action? | |
public func onStart(perform action: Action?) { | |
_onStart = action | |
} | |
public func onSuccess(perform action: Action?) { | |
_onSuccess = action | |
} | |
public func onFailure(perform action: ((LAContext.EvaluationFailure) -> Void)?) { | |
_onFailure = action | |
} | |
public func onCompletion(perform action: Action?) { | |
_onCompletion = action | |
} | |
public var reason: String | |
public var policy: LAPolicy = .deviceOwnerAuthentication | |
public var cancelTitle: String? | |
public var fallbackTitle: String? | |
public var wrappedValue: Action { | |
get { _action } | |
set { updateAction(newValue) } | |
} | |
public init(reason: String) { | |
self.reason = reason | |
self.wrappedValue = {} | |
} | |
public init(wrappedValue: @escaping Action) { | |
self.reason = "Biometrics are used to verify your identity." | |
self.wrappedValue = wrappedValue | |
} | |
public init(wrappedValue: @escaping Action, reason: String) { | |
self.reason = reason | |
self.wrappedValue = wrappedValue | |
} | |
private func tryExecute(action: @escaping Action) { | |
DispatchQueue.main.async { self._onStart?() } | |
LAContext().tryEvaluatePolicy(policy, reason: reason) { [weak self] result in | |
self?.handleEvaluation(result, with: action) | |
} | |
} | |
private func handleEvaluation(_ result: LAContext.EvaluationResult, with action: Action) { | |
switch result { | |
case .success: | |
action() | |
DispatchQueue.main.async { self._onSuccess?() } | |
case .failure(let error): | |
DispatchQueue.main.async { self._onFailure?(error) } | |
} | |
DispatchQueue.main.async { self._onCompletion?() } | |
} | |
private func updateAction(_ action: @escaping Action) { | |
_action = { [weak self] in self?.tryExecute(action: action) } | |
} | |
} |
This file contains 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 | |
extension LAContext { | |
typealias EvaluationResult = Result<Void, EvaluationFailure> | |
public enum EvaluationFailure: Error { | |
case wrapped(LAError) | |
case unknown | |
init(_ error: Error?) { | |
if let error = error as? LAError { self = .wrapped(error) } | |
else { self = .unknown } | |
} | |
} | |
/// Authentication request | |
/// | |
/// See | |
/// ``` | |
/// LAContext.evaluatePolicy | |
/// ``` | |
/// for more info | |
func tryEvaluatePolicy( | |
_ policy: LAPolicy, | |
reason: String, | |
reply: @escaping (EvaluationResult) -> Void | |
) { | |
var error: NSError? | |
if canEvaluatePolicy(policy, error: &error) { | |
evaluatePolicy(policy, localizedReason: reason) { result, error in | |
guard error == nil else { | |
reply(.failure(EvaluationFailure(error))) | |
return | |
} | |
reply(result | |
? .success(()) | |
: .failure(EvaluationFailure(error)) | |
) | |
} | |
} else if let error = error { | |
reply(.failure(EvaluationFailure(error))) | |
} else { | |
reply(.failure(EvaluationFailure(error))) | |
} | |
} | |
func getBiometryType(for policy: LAPolicy = .deviceOwnerAuthentication) -> LABiometryType? { | |
var error: NSError? | |
if canEvaluatePolicy(policy, error: &error) { | |
return biometryType | |
} else { | |
return nil | |
} | |
} | |
} | |
extension LABiometryType { | |
var isSupported: Bool { | |
if #available(iOS 11.2, *) { return self != .none } | |
if #available(iOS 11.0, *) { return self != .LABiometryNone } | |
else { return [.faceID, .touchID].contains(self) } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
Back to index