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) который выполняет те же действия и объект, что и исходная функция аутентификации.

Действие и объект передаются по цепочке до тех пор, пока пользователь не будет аутентифицирован и не будет выполнен.

Довольно хакерский код в целом, но, похоже, он мне подходит.

Другие вопросы по тегам