最近项目中,增加了利用TouchID验证登录的问题,以前也没有接触过,还是处于探索中完成需求吧,这里把踩过的坑谈一谈。
- 苹果在iOS8.0以后开放的TouchID接口,是包含在
#import <LocalAuthentication/LocalAuthentication.h>
头文件中。
- 一般情况下,一个APP中可能有好几个地方会用到TouchID解锁功能,因此我在项目中是写了一个工具类,直接用类方法来调用。
@interface XHRTouchIDTool : NSObject
// 当识别出现每一种情况是会发出通知
+ (void)validateTouchID;
@end
然后验证的每一种方式都会发出对应的通知,因为无论验证成功还是失败,必然会穿透好几个控制器或者是View去做一些事情,而且有可能是一对多的关系,因此通知最合适。
实现
- 首先判断版本号,必须8.0以上可用
// 判断系统是否是iOS8.0以上 8.0以上可用
if (!([[UIDevice currentDevice]systemVersion].doubleValue >= 8.0)) {
NSLog(@"系统不支持");
return;
}
- 创建LAContext对象开始验证
// 创建LAContext对象
LAContext *authenticationContext = [[LAContext alloc]init];
NSError *error = nil;
[authenticationContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
if (error.code == LAErrorTouchIDLockout && XHRDeviceVersion >= 9.0) {
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDLockout object:nil];
[authenticationContext evaluatePolicy:LAPolicyDeviceOwnerAuthentication localizedReason:@"重新开启TouchID功能" reply:^(BOOL success, NSError * _Nullable error) {
if (success) {
[self validateTouchID];
}
}];
return;
}
说明:这里验证的是iOS9.0以后开放的功能,就是默认当验证2个三次失败以后,系统会锁定TouchID硬件必须通过输入手机密码来解锁,所以这里利用递归的思想来解决这个问题。如果系统是iOS8的话,TouchID被锁定以后只能通过重启手机来重新开启。
注意点:LAPolicy这个参数的作用,这是一个枚举,有2个值如下:
typedef NS_ENUM(NSInteger, LAPolicy)
{
LAPolicyDeviceOwnerAuthenticationWithBiometrics NS_ENUM_AVAILABLE(NA, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0) = kLAPolicyDeviceOwnerAuthenticationWithBiometrics,
LAPolicyDeviceOwnerAuthentication NS_ENUM_AVAILABLE(10_11, 9_0) = kLAPolicyDeviceOwnerAuthentication
} NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0);
kLAPolicyDeviceOwnerAuthentication 这个值只有在iOS9.0以后才可以使用,利用这个值可以调出输入密码来解锁TouchID的界面。故以上要做9.0的版本判断。
如果设备可用,直接进入验证代码:
[authenticationContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"通过Home键验证已有手机指纹" reply:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
// 指纹识别错误调用分为以下情况,
// 点击取消按钮 : domain = com.apple.LocalAuthentication code = -2
// 点击输入密码按钮 : domain = com.apple.LocalAuthentication code = -3
// 输入密码重新进入指纹系统 : domain = com.apple.LocalAuthentication code = -8
// 指纹三次错误 : domain = com.apple.LocalAuthentication code = -1
// 指纹验证成功 : error = nil
if (error) {
switch (error.code) {
case LAErrorAuthenticationFailed:
JPLog(@"LAErrorAuthenticationFailed");
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDAuthenticationFailed object:nil];
break;
case LAErrorUserCancel:
// 点击取消按钮
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDCancel object:nil];
break;
case LAErrorUserFallback:
// 用户点击输入密码按钮
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDInputPassword object:nil];
break;
case LAErrorPasscodeNotSet:
//没有在设备上设置密码
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDErrorPasscodeNotSet object:nil];
break;
case LAErrorTouchIDNotAvailable:
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDNotAvailable object:nil];
//设备不支持TouchID
break;
case LAErrorTouchIDNotEnrolled:
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDNotEnrolled object:nil];
break;
//设备没有注册TouchID
case LAErrorTouchIDLockout:
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDLockout object:nil];
if (XHRDeviceVersion >= 9.0) {
[self validateTouchID];
}
break;
default:
break;
}
return ;
}
// 说明验证成功,如果要刷新UI必须在这里回到主线程
[[NSNotificationCenter defaultCenter]postNotificationName:XHRValidateTouchIDSuccess object:nil];
});
}];
这里有两点注意:第一、所有操作必须会到主线程,以为系统的验证是在子线程。
第二、所有的错误处理是通过一个枚举来判断处理的,具体作用注释已经很清楚了,枚举值如下:
typedef NS_ENUM(NSInteger, LAError)
{
/// Authentication was not successful, because user failed to provide valid credentials.
LAErrorAuthenticationFailed = kLAErrorAuthenticationFailed,
/// Authentication was canceled by user (e.g. tapped Cancel button).
LAErrorUserCancel = kLAErrorUserCancel,
/// Authentication was canceled, because the user tapped the fallback button (Enter Password).
LAErrorUserFallback = kLAErrorUserFallback,
/// Authentication was canceled by system (e.g. another application went to foreground).
LAErrorSystemCancel = kLAErrorSystemCancel,
/// Authentication could not start, because passcode is not set on the device.
LAErrorPasscodeNotSet = kLAErrorPasscodeNotSet,
/// Authentication could not start, because Touch ID is not available on the device.
LAErrorTouchIDNotAvailable = kLAErrorTouchIDNotAvailable,
/// Authentication could not start, because Touch ID has no enrolled fingers.
LAErrorTouchIDNotEnrolled = kLAErrorTouchIDNotEnrolled,
/// Authentication was not successful, because there were too many failed Touch ID attempts and
/// Touch ID is now locked. Passcode is required to unlock Touch ID, e.g. evaluating
/// LAPolicyDeviceOwnerAuthenticationWithBiometrics will ask for passcode as a prerequisite.
LAErrorTouchIDLockout NS_ENUM_AVAILABLE(10_11, 9_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0) = kLAErrorTouchIDLockout,
/// Authentication was canceled by application (e.g. invalidate was called while
/// authentication was in progress).
LAErrorAppCancel NS_ENUM_AVAILABLE(10_11, 9_0) = kLAErrorAppCancel,
/// LAContext passed to this call has been previously invalidated.
LAErrorInvalidContext NS_ENUM_AVAILABLE(10_11, 9_0) = kLAErrorInvalidContext
} NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0);
其实这个error本身并没有什么复杂逻辑可言,但是登录的时候一定要注意版本和设备的判断,以及验证失败时候TouchID设备被锁定的问题。我是用通过一个单独的验证控制器和这个工具类来完成项目中的验证需求的。
作者:胥鸿儒
如有纰漏,请大家指正。
Demo地址:https://github.com/xuhongru/TouchID