Search code examples
iosobjective-ctouch-id

iOS 8 Touch ID error "User interaction is required."


I have been working on integrating Touch ID support into an app I am working on. It is however acting very inconsistent. One of the common issues I am seeing is on a fresh app launch it works as expected, but then on backgrounding the app, and bringing it to the foreground I am getting an error back from

evaluatePolicy:localizedReason:reply:

It does not even make a lot of sense (I never see the touchid alert)

Error Domain=com.apple.LocalAuthentication Code=-1004 "User interaction is required." UserInfo=0x171470a00 {NSLocalizedDescription=User interaction is required.}

I have tried presenting the touchid alert when the app is already running, when its just foregrounded, does not seem to matter. Its broken on every time after the initial app launch.

Anyone else running into this?

For reference, here is the code I am using:

if (_useTouchId && [LAContext class]) {
    LAContext *myContext = [[LAContext alloc] init];
    NSError *authError = nil;

    if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
        _didPresentTouchId = YES;
        [myContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"Use your Touch ID to open *****" reply:^(BOOL success, NSError *error) {
            dispatch_async(dispatch_get_main_queue(), ^ {
                if (success) {
                    _isClosing = YES;

                    [self hide];
                    if (_successBlock) {
                        _successBlock();
                    }
                }
                else if (error && error.code != -2 && error.code != -3 && error.code != -1004) {
                    [[[UIAlertView alloc] initWithTitle:@"Error" message:@"Authentication failed, please enter your Pin" delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil] show];
                }
                else {
                    if (error) {
                        DDLogError(@"TouchID error: %@", error.description);
                    }

                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .6 * NSEC_PER_SEC), dispatch_get_main_queue(), ^ {
                        [self keyboardButtonTouched];
                    });
                }
            });
        }];
    }
}

Solution

  • Usually PIN view controllers are pushed before entering background in:

    - (void)applicationDidEnterBackground:(UIApplication *)application
    

    So app's inner information won't appear when paging through app preview images (home button double tap). I guess you are doing something similar.

    The problem is that LocalAuthentication's new API requires the calling viewController to be visible. This is why you shouldn't call your "showTouchID" function before resigning to background. Instead call "showTouchID" function when entering foreground:

    - (void)applicationWillEnterForeground:(UIApplication *)application
    

    And it should work. Don't forget to call it also when app is first launched (in which case ..willEnterForeground will not get called).