Mark L
Mark L

Reputation: 759

Setting the TouchID "Enter Password" fallback to start editing UITextField

I got touchID set up and working fine in my application.

However I'd like to change the functionality of "Enter Password".

When creating my authentication I was following the tutorial from: http://www.appcoda.com/touch-id-api-ios8/

However they user an alertView for the "Enter Password" option.

I'd like to dismiss the touchID alertview and make my passwordTextField become the firstResponder.

Naturally I tried:

self.passwordTextField.becomeFirstResponder()

But that causes an error:

2015-04-09 10:48:42.309 Formula Stocks[3933:964941] *** Assertion failure in void _UIPerformResizeOfTextViewForTextContainer(NSLayoutManager *, UIView<NSTextContainerView> *, NSTextContainer *, NSUInteger)(), /SourceCache/UIFoundation/UIFoundation-371.13/UIFoundation/TextSystem/NSLayoutManager_Private.m:1547
2015-04-09 10:48:42.312 Formula Stocks[3933:964941] <NSXPCConnection: 0x1701061e0> connection to service named com.apple.CoreAuthentication.daemon: Warning: Exception caught during decoding of received reply to message 'evaluatePolicy:options:reply:', dropping incoming message and calling failure block.

Exception: Only run on the main thread!

Here's my function for authentication:

func requestFingerprintAuthentication() {
    // Get the local authentication context.
    let context = LAContext()

    // Declare a NSError variable.
    var error: NSError?

    // Set the reason string that will appear on the authentication alert.
    var reasonString = "Sign In."

    // Check if the device can evaluate the policy.
    if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
        [context .evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in

            if success {
                NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
                    println("successfull signin with touchID")
                    self.emailTextField.text = emailID as! String
                    self.passwordTextField.text = passwordID as! String
                    self.signIn(self.signInButton)
                })
            }
            else{
                // If authentication failed then show a message to the console with a short description.
                // In case that the error is a user fallback, then show the password alert view.
                println(evalPolicyError?.localizedDescription)

                switch evalPolicyError!.code {

                case LAError.SystemCancel.rawValue:
                    println("Authentication was cancelled by the system")

                case LAError.UserCancel.rawValue:
                    println("Authentication was cancelled by the user")

                case LAError.UserFallback.rawValue:
                    println("User selected to enter custom password")
                    self.passwordTextField.becomeFirstResponder()

                default:
                    println("Authentication failed")
                    self.passwordTextField.becomeFirstResponder()
                }
            }

        })]
    }
    else{
        // If the security policy cannot be evaluated then show a short message depending on the error.
        switch error!.code{

        case LAError.TouchIDNotEnrolled.rawValue:
            println("TouchID is not enrolled")

        case LAError.PasscodeNotSet.rawValue:
            println("A passcode has not been set")

        default:
            // The LAError.TouchIDNotAvailable case.
            println("TouchID not available")
        }

        // Optionally the error description can be displayed on the console.
        println(error?.localizedDescription)

        // Show the custom alert view to allow users to enter the password.
        //self.showPasswordAlert()
        self.passwordTextField.becomeFirstResponder()
    }
}

It does print out

Optional("Fallback authentication mechanism selected.")
User selected to enter custom password

So I know it's calling the correct .case

Any help getting it to select my UITextField would be greatly appreciated!

Upvotes: 7

Views: 3355

Answers (1)

Paul Cezanne
Paul Cezanne

Reputation: 8741

You need to put becomeFirstResponder on the main thread. iOS require all UI operations to be on the main thread. You often get UI accidentally off the main thread when using closures (blocks in Objective-C). The usual bug you get is that the UI "freezes" for a few seconds. That's the classic case of not being on the main thread when you need to be.

Your case is a little different, but the log in the Console gave the answer: "Exception: Only run on the main thread!"

Upvotes: 4

Related Questions