Skip to content

Instantly share code, notes, and snippets.

@VNGXR
Forked from maximkrouk/Authenticated.swift
Created March 18, 2020 12:38
Show Gist options
  • Select an option

  • Save VNGXR/169f0a286b494d65829bf028209a5878 to your computer and use it in GitHub Desktop.

Select an option

Save VNGXR/169f0a286b494d65829bf028209a5878 to your computer and use it in GitHub Desktop.
Property wrapper for secured actions
import Foundation
import LocalAuthentication
@propertyWrapper
public class Authenticated {
public typealias Action = () -> Void
private var _action: Action = {}
public var reason: String
public var onStart: Action = {}
public var onCompletion: Action = {}
public var onSuccess: Action = {}
public var onFailure: (LAContext.EvaluationFailure) -> Void = { _ in }
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) }
}
}
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 }
return self == .touchID
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment