iOS --- Touch ID指纹解锁

最近在项目中刚好用到了TouchId指纹解锁功能,之前也没有接触过,立马百度看看究竟是要如何使用,发现其实也不是很复杂。
文章后面有封装的工具方法,可以直接copy使用。

下面开始跟大家分享一下:

一、使用要点:

(1)需要导入库 LocalAuthentication.framework
(2)引入头文件 #import <LocalAuthentication/LocalAuthentication.h>
(3)指纹解锁只支持iOS 8及以上的版本
(4)如果指纹验证多次错误会被锁定,只能通过输入手机密码来重新启用。iOS 9 提供了一个方法,可以直接调起密码输入界面。
(5)所有操作必须要回到主线程,因为系统的验证是在子线程。
(6)所有的错误处理是通过一个枚举来判断处理的,具体作用看注释

二、代码要点

(1)这是判断系统版本是否大于iOS 8

if (!(MQ_CURRENT_DEVICE_SYSTEM_VERSION >= 8.0)) {
        result(NO, MQ_TOUCHID_ERROR_SYSTEM_NOT_SUPPORT);
        return;
    }

(2)判断是否可用

// 返回bool值
[authenContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]

(3)下面的就是重要的点 :

指纹解锁多次失败以后,系统会锁定TouchID硬件必须通过输入手机密码来解锁,如果系统是iOS8的话,TouchID被锁定以后只能通过重启手机来重新开启。

//系统提供的两个验证的枚举
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的界面。

 [authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                  localizedReason:TOUCHID_NOTICE_MESSAGE
                            reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                  //已经重新解锁Touchid
                                } 
                            }];

开始指纹解锁代码

[authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                  localizedReason:TOUCHID_NOTICE_MESSAGE
                            reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                   // 解锁成功
                                } 
                            }];

(4)看看系统的错误枚举

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 NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use LAErrorBiometryNotAvailable") = kLAErrorTouchIDNotAvailable,

    /// Authentication could not start, because Touch ID has no enrolled fingers.
    LAErrorTouchIDNotEnrolled NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use LAErrorBiometryNotEnrolled") = 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_DEPRECATED(10_11, 10_13, 9_0, 11_0, "use LAErrorBiometryLockout")
        __WATCHOS_DEPRECATED(3.0, 4.0, "use LAErrorBiometryLockout") __TVOS_DEPRECATED(10.0, 11.0, "use LAErrorBiometryLockout") = 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,

    /// Authentication could not start, because biometry is not available on the device.
    LAErrorBiometryNotAvailable NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryNotAvailable,

    /// Authentication could not start, because biometry has no enrolled identities.
    LAErrorBiometryNotEnrolled NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryNotEnrolled,

    /// Authentication was not successful, because there were too many failed biometry attempts and
    /// biometry is now locked. Passcode is required to unlock biometry, e.g. evaluating
    /// LAPolicyDeviceOwnerAuthenticationWithBiometrics will ask for passcode as a prerequisite.
    LAErrorBiometryLockout NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryLockout,
    
    /// Authentication failed, because it would require showing UI which has been forbidden
    /// by using interactionNotAllowed property.
    LAErrorNotInteractive API_AVAILABLE(macos(10.10), ios(8.0), watchos(3.0), tvos(10.0)) = kLAErrorNotInteractive,
} NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0);

三、这是我封装的工具类

(1)MQTouchID.h文件

//
//  MQTouchID.h
//  Test
//
//  Created by PasserMontanus on 2018/3/21.
//  Copyright © 2018年 lgl. All rights reserved.
//
#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, MQ_TOUCHID_ERROR) {
    // 系统不支持 要大于 8.0
    MQ_TOUCHID_ERROR_SYSTEM_NOT_SUPPORT = 0,
    // 验证成功
    MQ_TOUCHID_ERROR_SUCCESS = 1,
    // 系统取消授权,如其他APP切入
    MQ_TOUCHID_ERROR_SYSTEM_CANCEL = -4,
    // 用户取消验证Touch ID
    MQ_TOUCHID_ERROR_USER_CANCEL = -2,
    // 用户选择输入密码
    MQ_TOUCHID_ERROR_USER_FALLBACK = -3,
    //授权失败
    MQ_TOUCHID_ERROR_FAILED = -1,
    //系统未设置密码 这个密码在用户指纹多次验证失败的时候要用他来重新激活
    MQ_TOUCHID_ERROR_PASSCODE_NOT_SET = -5,
    //设备Touch ID不可用,例如未打开
    MQ_TOUCHID_ERROR_NOT_AVAILABLE = -6,
    //设备Touch ID不可用,用户没有录入指纹
    MQ_TOUCHID_ERROR_NOT_ENROLLED = -7,
    //身份验证失败,因为它需要显示已被禁止的UI
    MQ_TOUCHID_ERROR_NOT_INTERACTIVE = -1004,
    // 设备Touch ID被锁定,输入错误的次数过多 这个时候需要密码来重新激活
    MQ_TOUCHID_ERROR_LOCKOUT = -8 ,
    //应用程序已取消身份验证
    MQ_TOUCHID_ERROR_APP_CANCEL = -9,
    // 传递给此调用的LAContext先前已失效。
    MQ_TOUCHID_ERROR_INVALID_CONTEXT = -10
};

typedef void(^MQTouchIDResult)(BOOL isSuccess, MQ_TOUCHID_ERROR error_code);

@interface MQTouchID : NSObject

/**
 开始验证 TouchID 指纹解锁 (需要导入 LocalAuthentication.framework)
 
 注意: 结果回调以后的操作都需要回到主线程 这个验证是在子线程里面执行的
 */
+ (void)validateTouchID:(MQTouchIDResult)result;

@end

(2)MQTouchID.m文件

//
//  MQTouchID.m
//  Test
//
//  Created by PasserMontanus on 2018/3/21.
//  Copyright © 2018年 lgl. All rights reserved.
//
#import "MQTouchID.h"
#import <LocalAuthentication/LocalAuthentication.h>

#define MQ_CURRENT_DEVICE_SYSTEM_VERSION ([[UIDevice currentDevice]systemVersion].doubleValue)

static NSString  * TOUCHID_NOTICE_MESSAGE          = @"通过Home键验证已有的手机指纹";
static NSString  * TOUCHID_NOTICE_RESTART_MESSAGE  = @"重新开启TouchID功能";


@implementation MQTouchID


+ (void)validateTouchID:(MQTouchIDResult)result {
    if (!(MQ_CURRENT_DEVICE_SYSTEM_VERSION >= 8.0)) {
        result(NO, MQ_TOUCHID_ERROR_SYSTEM_NOT_SUPPORT);
        return;
    }
    LAContext *authenContext = [[LAContext alloc]init];
    NSError* error = nil;
    if ([authenContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { // 可用
        [self startValidateTouchID:authenContext result:result];
    } else { // 不可用
        MQ_TOUCHID_ERROR erroCode =  [self errorCode:error.code];
        [self processingTouchIdError:authenContext erroCode:erroCode result:result];
    }
}


+ (void)startValidateTouchID:(LAContext *)authenContext result:(MQTouchIDResult)result {
    [authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                  localizedReason:TOUCHID_NOTICE_MESSAGE
                            reply:^(BOOL success, NSError * _Nullable error) {
                                if (success) {
                                    result(YES,MQ_TOUCHID_ERROR_SUCCESS);
                                } else {
                                    MQ_TOUCHID_ERROR erroCode =  [self errorCode:error.code];
                                    [self processingTouchIdError:authenContext erroCode:erroCode result:result];
                                }
                            }];
}

+ (void)processingTouchIdError:(LAContext *)authenContext
                      erroCode:(MQ_TOUCHID_ERROR)erroCode
                        result:(MQTouchIDResult)result {
    
    if (@available(iOS 9.0, *)) {
        if (erroCode == MQ_TOUCHID_ERROR_LOCKOUT) {
            [authenContext evaluatePolicy:LAPolicyDeviceOwnerAuthentication
                          localizedReason:TOUCHID_NOTICE_RESTART_MESSAGE
                                    reply:^(BOOL success, NSError * _Nullable error) {
                                        if (success) {
                                            [self validateTouchID:result];
                                        } else {
                                            result(NO, MQ_TOUCHID_ERROR_USER_CANCEL);
                                        }
                                    }];
        
        } else {
            result(NO, erroCode);
        }
    } else {
        result(NO, erroCode);
    }
}

+ (MQ_TOUCHID_ERROR)errorCode:(NSInteger )errorCode {
    switch (errorCode) {
        case LAErrorSystemCancel:{ // 系统取消授权,如其他APP切入
            return MQ_TOUCHID_ERROR_SYSTEM_CANCEL;
            break;
        }
        case LAErrorUserCancel:{ // 用户取消验证Touch ID
            return MQ_TOUCHID_ERROR_USER_CANCEL;
            break;
        }
        case LAErrorUserFallback:{ // 用户选择输入密码
            return MQ_TOUCHID_ERROR_USER_FALLBACK;
            break;
        }
        case LAErrorAuthenticationFailed:{ // //授权失败
            return MQ_TOUCHID_ERROR_FAILED;
            break;
        }
        case LAErrorPasscodeNotSet: { //系统未设置密码 这个密码在用户指纹多次验证失败的时候要用他来重新激活
            return MQ_TOUCHID_ERROR_PASSCODE_NOT_SET;
            break;
        }
        case LAErrorTouchIDNotAvailable: { //设备Touch ID不可用,例如未打开
            return MQ_TOUCHID_ERROR_NOT_AVAILABLE;
            break;
        }
        case LAErrorTouchIDNotEnrolled: { //设备Touch ID不可用,用户没有录入指纹
            return MQ_TOUCHID_ERROR_NOT_ENROLLED;
            break;
        }
        case LAErrorNotInteractive: { //   身份验证失败,因为它需要显示已被禁止的UI
            return MQ_TOUCHID_ERROR_NOT_INTERACTIVE;
            break;
        }
        default:{
            if (@available(iOS 9.0, *)) {
                if (errorCode == LAErrorTouchIDLockout) { // 设备Touch ID被锁定,输入错误的次数过多 这个时候需要密码来重新激活
                    return MQ_TOUCHID_ERROR_LOCKOUT;
                    break;
                } else {
                    if (errorCode == LAErrorInvalidContext) {
                        return MQ_TOUCHID_ERROR_INVALID_CONTEXT;
                        break;
                    } else if (errorCode == LAErrorAppCancel) {
                        return MQ_TOUCHID_ERROR_APP_CANCEL;
                        break;
                    } else {
                        return MQ_TOUCHID_ERROR_FAILED;
                        break;
                    }
                }
            } else {
                return MQ_TOUCHID_ERROR_FAILED;
                break;
            }
        }
    }
}

@end

四、最后结语

(1)最后的结果回调以后的操作一定要在主线程,系统验证在子线程进行
(2)以上就是我的一点小总结,欢迎大家指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 用到的组件 1、通过CocoaPods安装 2、第三方类库安装 3、第三方服务 友盟社会化分享组件 友盟用户反馈 ...
    SunnyLeong阅读 14,607评论 1 180
  • 在开始代码之前,先认识一下LAPolicy。 LAPolicy是一个枚举,有两个值 第一个枚举值,用户验证失败3次...
    夜凉听风雨阅读 6,358评论 0 16
  • 一直想玩玩指纹解锁,近期时间比较闲,就研究了一下这个指纹解锁。这个功能实现起来还是很简单的,虽然看起来比较高大上,...
    weicyNO_1阅读 3,200评论 0 3
  • 凌晨一点、敢小姐放下手中的kindle、拿起已经黑屏的手机。点击微信、开始着睡前最后一次刷朋友圈。突然跳出一个好友...
    敢姑娘阅读 1,472评论 29 7
  • 是的,我成为不了一个诗人了 当我的右手做过爱以后 连纸笔都抓握不住 诗句都书写不成行,包括字词 是的,我成为不了一...
    景大别阅读 244评论 0 0