-
-
Save VNGXR/169f0a286b494d65829bf028209a5878 to your computer and use it in GitHub Desktop.
Property wrapper for secured actions
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 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) } | |
| } | |
| } |
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 | |
| 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