iOS - функция аутентификации пользователя, которая возвращает Bool
В конечном счете, я хочу иметь одну функцию (или, возможно, функцию в отдельном классе), которая предлагает пользователю выполнить аутентификацию через TouchID, затем пароль и, если любой из них успешен, возвращает истинное логическое значение.
Я выяснил аутентификацию в основном, однако я не могу заставить функцию возвращать логическое значение, вот примерно то, что я имею до сих пор:
Функция аутентификации пользователя:
func authenticateUser() -> Bool {
let context = LAContext()
var error: NSError?
let reasonString = "Authentication is needed to access your places."
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success, policyError) -> Void in
if success {
print("touchID authentication succesful")
} else {
switch policyError!.code {
case LAError.UserFallback.rawValue:
print("User selected to enter password.")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
default:
print("Authentication failed! :(")
}
}
})
} else {
print(error?.localizedDescription)
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
}
return true
}
Он просто должен возвращать true для тестирования. Однако я хотел бы, чтобы он возвращал значение true при успешной аутентификации. Я не могу поместить возвращение в context.evaluatePolicy
потому что это внутри метода блока. Есть ли другой способ сделать то, что я хочу? Или я поступаю об этом совершенно неправильно?
Также для справки вот мой showPasswordAlert
функция:
func showPasswordAlert() {
let alertController = UIAlertController(title: "Passcode", message: "Please enter your passcode.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default) { (action) -> Void in
if let textField = alertController.textFields?.first as UITextField? {
if let passcode = self.keychainWrapper.myObjectForKey("v_Data") as? String {
if textField.text == passcode {
print("Authentication successful! :) ")
} else {
}
}
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alertController.addAction(defaultAction)
alertController.addAction(cancelAction)
alertController.addTextFieldWithConfigurationHandler { (textField) -> Void in
textField.placeholder = "Enter passcode..."
textField.textAlignment = NSTextAlignment.Center
textField.secureTextEntry = true
textField.keyboardType = UIKeyboardType.NumberPad
}
self.presentViewController(alertController, animated: true, completion: nil)
}
Итак, в моей голове я думаю следующее: showPasswordAlert
может также вернуть истинное логическое значение для authenticateUser
и тогда это, в свою очередь, вернет истинное логическое значение туда, где authenticateUser
функция вызывается. Я знаю, что есть более простой способ сделать это, но я просто хотел бы, чтобы это работало сейчас.
2 ответа
Также возникла эта проблема, я закончил тем, что создал структуру с именем Authentication, в которой есть статическая функция authenticate, которая передает контроллер представления из того места, где вы его вызываете, и функцию обратного вызова:
import LocalAuthentication
import UIKit
struct Authentication {
static func authenticate(viewController: UIViewController, callback:
@escaping (Bool) -> ()) {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
let reason = "Please Authenticate"
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) {
[weak viewController] success, authenticationError in
guard let viewController = viewController else {
return
}
DispatchQueue.main.async {
if success {
callback(true)
} else {
let ac = UIAlertController(title: "Authentication failed", message: "Please try again", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
viewController.present(ac, animated: true)
callback(false)
}
}
}
} else {
let ac = UIAlertController(title: "Touch ID not available", message: "Your device is not configured for Touch ID.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
viewController.present(ac, animated: true)
callback(false)
}
}
}
Тогда называя это:
Authentication.authenticate(viewController: parentViewController, callback: {
[weak self] (authenticated: Bool) in
if authenticated {
self?.yourFunctionHere()
}
})
Поэтому после долгих проб и ошибок я придумала, возможно, лучшее решение для меня на данный момент.
Кажется, так как evaluatePolicy
и запускаются асинхронно, вы не можете возвращать переменные из них или обращаться к переменным. Однако вы можете вызывать селекторы из этих блоков (почему я не знаю).
Итак, мое текущее решение на момент написания этого поста - вызвать функцию authenticate как таковую:
func authenticateUserWithAction(actionSelector: Selector, object: AnyObject){}
Я передаю ему действие (объявленное в другом месте класса, но в основном то, что вы хотите сделать, если аутентификация прошла успешно) и объект. Объект просто в том случае, если действие требует, чтобы что-то было передано функции. Так, например, в моем приложении после аутентификации представлен viewController, и объект в этом viewController устанавливается на объект в исходном viewController. Этот объект передается в функцию authenticate.
Изнутри функция аутентификации пользователя я могу вызвать authenticateUserWithPasscode(actionSelector: Selector, object: AnyObject)
который выполняет те же действия и объект, что и исходная функция аутентификации.
Действие и объект передаются по цепочке до тех пор, пока пользователь не будет аутентифицирован и не будет выполнен.
Довольно хакерский код в целом, но, похоже, он мне подходит.