12

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];
                    });
                }
            });
        }];
    }
}
Stephen Bradley
  • 373
  • 2
  • 10
  • 1
    getting this too. Really anoying – gallileo Oct 02 '14 at 00:25
  • 3
    I'm experiencing the same error but in my case even when the app is already active and in the foreground. I am working on a framework which is used by another app but my code works fine when I tried it on a standalone project. I have the code placed in the viewDidAppear of the initial view controller but it always results to an error. – schystz Feb 16 '15 at 07:02
  • @schystz I have the same... I swear it is after I changed my bundle id.... Have you found a soluttion?!! – maxisme Sep 07 '15 at 16:11
  • @Maximilian As far as I remember, the fault was somewhere else in my code. It was a timing issue, double check and make sure that you are running the block of code in the main thread and when the app is in active state. I ended up putting the code in the viewDidAppear after all. – schystz Sep 07 '15 at 22:28

2 Answers2

7

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).

HeTzi
  • 916
  • 9
  • 18
  • This is more or less what was happening. I ended up just dropping the splash screen into the main windows view on backgrounding (so you can not see the content) and then presenting the PIN controller once the app was opened, and removing the splash screen. Works well. – Stephen Bradley Oct 23 '14 at 22:25
6

@hetzi answer really helped me, but I have more to add on this.

Basically this error happens when your app is woken up from background and somewhere on your code you are asking for Touch ID (my case is the local authentication type, I haven't tested with the keychain type). There's no way the user can interact with Touch ID prompted while the app is running on background, hence the error message.

User interaction is required.

The reasons my app was coming from background were: Push Notifications or Apple Watch.

My fix is doing something like this on the viewDidLoad method of my initial VC:

if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
    [self promptForTouchID];
}

I've used != because, when your app first launches it is in the UIApplicationStateInactive state. And that state doesn't generate a Touch ID error because the prompt will appear.

I also call [self promptForTouchID] on a notification of UIApplicationWillEnterForegroundNotification, but since you know that the app will enter foreground, there's no need to check here.

Lucien
  • 8,263
  • 4
  • 30
  • 30