最近公司的 app 要使用指紋支付了;整體來講仍是蠻順利的;可是中間有遇到一些坑;下面就對坑進行彙總;html
點開這個LocalAuthentication.framework
,發現裏面主要有這麼幾個東西git
- LAContext.h - LAError.h - LAPublicDefines.h - LocalAuthentication.h
這個沒什麼可講的吧,代碼就兩行,一行導入LAContext.h
,一行導入LAError.h
,這個LocalAuthentication
類是暴露出來方便開發者調用的類。github
先從簡單的開始講吧,首先是LAPublicDefines.h
,從名字上來看是公共宏定義類,裏面包含了許多定義好的宏,這些宏會在LAContext.h
獲得使用。數據庫
// // LAPublicDefines.h // LocalAuthentication // // Copyright (c) 2014 Apple. All rights reserved. // #ifndef LocalAuthentication_LAPublicDefines_h #define LocalAuthentication_LAPublicDefines_h // Policies #define kLAPolicyDeviceOwnerAuthenticationWithBiometrics 1 #define kLAPolicyDeviceOwnerAuthentication 2 // Options #define kLAOptionUserFallback 1 #define kLAOptionAuthenticationReason 2 // Credential types #define kLACredentialTypePasscode -1 #define kLACredentialTypePassphrase -2 #define kLACredentialCTKPIN -3 // Error codes #define kLAErrorAuthenticationFailed -1 #define kLAErrorUserCancel -2 #define kLAErrorUserFallback -3 #define kLAErrorSystemCancel -4 #define kLAErrorPasscodeNotSet -5 #define kLAErrorTouchIDNotAvailable -6 #define kLAErrorTouchIDNotEnrolled -7 #define kLAErrorTouchIDLockout -8 #define kLAErrorAppCancel -9 #define kLAErrorInvalidContext -10 // Error domain #define kLAErrorDomain "com.apple.LocalAuthentication" #endif
這個類其實也不用贅述,就是一個枚舉,裏面寫的是錯誤的類型,其實就是把上面的kLAError宏寫進這個枚舉了,具體代碼註釋寫的很清晰,大概翻譯了一下swift
typedef NS_ENUM(NSInteger, LAError) { LAErrorAuthenticationFailed, // 驗證信息出錯,就是說你指紋不對 LAErrorUserCancel // 用戶取消了驗證 LAErrorUserFallback // 用戶點擊了手動輸入密碼的按鈕,因此被取消了 LAErrorSystemCancel // 被系統取消,就是說你如今進入別的應用了,不在剛剛那個頁面,因此無法驗證 LAErrorPasscodeNotSet // 用戶沒有設置TouchID LAErrorTouchIDNotAvailable // 用戶設備不支持TouchID LAErrorTouchIDNotEnrolled // 用戶沒有設置手指指紋 LAErrorTouchIDLockout // 用戶錯誤次數太多,如今被鎖住了 LAErrorAppCancel // 在驗證中被其餘app中斷 LAErrorInvalidContext // 請求驗證出錯 } NS_ENUM_AVAILABLE(10_10, 8_0);
重頭戲來了,想在本身的項目中使用TouchID,就要用到LAContext這個類裏面的方法首先映入眼簾的是一個NS_ENUM枚舉LAPolicy。api
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);
第一個枚舉LAPolicyDeviceOwnerAuthenticationWithBiometrics就是說,用的是手指指紋去驗證的;
NS_ENUM_AVAILABLE(NA, 8_0)
iOS8 可用
第二個枚舉LAPolicyDeviceOwnerAuthentication少了WithBiometrics則是使用TouchID或者密碼驗證,默認是錯誤兩次指紋或者鎖定後,彈出輸入密碼界面;NS_ENUM_AVAILABLE(10_11, 9_0)
iOS 9可用ruby
首先暴露出來的幾個方法,注意這裏都是實例方法,因此須要建立一個實例對象去才能調用,使用LAContext *context = [LAContext alloc] init]
;建立一個LAContext
對象。微信
canEvaluatePolicy:error:方法用來檢查當前設備是否可用touchID,返回一個BOOL值
evaluatePolicy:localizedReason:reply:調用驗證方法,注意這裏的三個參數:
第一個參數policy是要使用上面那個LAPolicy的枚舉
第二個參數localizedReason是NSString類型的驗證理由
第三個參數reply則是一個回調Block,block內有一個BOOL類型的success判斷是否成功驗證,還有一個用於判斷錯誤信息的NSError類型的error
invalidate方法用來廢止這個contextapp
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError * __autoreleasing *)error __attribute__((swift_error(none))); - (void)evaluatePolicy:(LAPolicy)policy localizedReason:(NSString *)localizedReason reply:(void(^)(BOOL success, NSError * __nullable error))reply; - (void)invalidate;
枚舉LACredentialType,LAAccessControlOperation
,這個東西和下面的幾個方法我查了好久也沒弄明白用在哪,蘋果官方文檔也看的不太懂,枚舉中只有一個LACredentialTypeApplicationPassword。
不過經過這個NS_ENUM_AVAILABLE(10_11, 9_0)還有方法後面的NS_AVAILABLE(10_11, 9_0)知道這個枚舉和這兩個方法只能在OS X 10.11和iOS 9.0以上版本使用,因此多是比較新的東西,後面蘋果還會對他擴充吧。
下面是方法的說明:less
// 目前額外加密就一種就是應用密碼 // 輸入進去將會是 UTF-8 的字符串 typedef NS_ENUM(NSInteger, LACredentialType) { LACredentialTypeApplicationPassword = 0, } NS_ENUM_AVAILABLE(10_11, 9_0);
// 如下方法據我理解應該是:能夠在驗證Touch ID以後額外加密? // 設置解鎖額外加密憑證 - (BOOL)setCredential:(nullable NSData *)credential type:(LACredentialType)type NS_AVAILABLE(10_11, 9_0); // 判斷加密憑證是否設置成功 - (BOOL)isCredentialSet:(LACredentialType)type NS_AVAILABLE(10_11, 9_0); // 經過Touch ID來驗證加密憑證是否經過 - (void)evaluateAccessControl:(SecAccessControlRef)accessControl operation:(LAAccessControlOperation)operation localizedReason:(NSString *)localizedReason reply:(void(^)(BOOL success, NSError * __nullable error))reply NS_AVAILABLE(10_11, 9_0);
typedef NS_ENUM(NSInteger, LAAccessControlOperation) { // 建立額外加密 LAAccessControlOperationCreateItem, // 使用額外加密 LAAccessControlOperationUseItem, // 建立額外加密key LAAccessControlOperationCreateKey, // 使用額外加密key簽名 LAAccessControlOperationUseKeySign } NS_ENUM_AVAILABLE(10_11, 9_0);
屬性的話,這裏有5個
@property (nonatomic, nullable, copy) NSString *localizedFallbackTitle; @property (nonatomic, nullable, copy) NSString *localizedCancelTitle NS_AVAILABLE(10_12, 10_0); @property (nonatomic, nullable) NSNumber *maxBiometryFailures NS_DEPRECATED_IOS(8_3, 9_0) __WATCHOS_UNAVAILABLE __TVOS_UNAVAILABLE; @property (nonatomic, nullable, readonly) NSData *evaluatedPolicyDomainState NS_AVAILABLE(10_11, 9_0) __WATCHOS_UNAVAILABLE __TVOS_UNAVAILABLE; @property (nonatomic) NSTimeInterval touchIDAuthenticationAllowableReuseDuration NS_AVAILABLE(NA, 9_0) __WATCHOS_UNAVAILABLE __TVOS_UNAVAILABLE;
localizedFallbackTitle
能夠設置驗證TouchID時彈出Alert的輸入密碼按鈕的標題localizedCancelTitle
能夠設置驗證TouchID時彈出Alert的取消按鈕的標題(iOS10纔有)maxBiometryFailures
最大指紋嘗試錯誤次數。 這個屬性咱們能夠看到他後面寫了NS_DEPRECATED_IOS(8_3, 9_0)
,說明這個屬性在iOS 8.3被引入,在iOS 9.0被廢棄,因此若是系統版本高於9.0是沒法使用的。evalueatedPolicyDomainState
這個跟能夠檢測你的指紋數據庫的變化,增長或者刪除指紋這個屬性會作出相應的反應touchIDAuthenticationAllowableReuseDuration
這個屬性應該是相似於支付寶的指紋開啓應用,若是你打開他解鎖以後,按Home鍵返回桌面,再次進入支付寶是不須要錄入指紋的。由於這個屬性能夠設置一個時間間隔,在時間間隔內是不須要再次錄入。默認是0秒,最長能夠設置5分鐘。
iOS 9 以前是沒有LAErrorTouchIDLockout
鎖定這個選項的,默認錯誤5次後;第6次驗證是自動彈出輸入密碼界面;
iOS 9 以後鎖定指紋識別以後,若是須要當即彈出輸入密碼界面須要使用LAPolicyDeviceOwnerAuthentication
這個屬性從新發起驗證
canEvaluatePolicy:error:
方法用來檢查當前設備是否可用touchID,返回一個BOOL值;不會彈驗證指紋密碼框evaluatePolicy:localizedReason:reply:
調用驗證方法,會彈驗證指紋密碼框feedBackTitle = @""
該屬性, iOS 10 才能夠進行設置,iOS 之前是不能夠進行設置的
[SVProgressHUD show];
LAContext *context = [[LAContext alloc]init];//使用 new 不會給一些屬性初始化賦值 context.localizedFallbackTitle = @"輸入密碼";//@""能夠不讓 feedBack 按鈕顯示 //LAPolicyDeviceOwnerAuthenticationWithBiometrics [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"請驗證已有指紋" reply:^(BOOL success, NSError * _Nullable error) { [SVProgressHUD dismiss]; //SVProgressHUD dismiss 須要 0.15纔會消失;因此dismiss 後進行下一步操做;可是0.3是適當延長時間;留點餘量 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (success) { NSLog(@"指紋識別成功"); // 指紋識別成功,回主線程更新UI dispatch_async(dispatch_get_main_queue(), ^{ //成功操做 }); } if (error) { //指紋識別失敗,回主線程更新UI dispatch_async(dispatch_get_main_queue(), ^{ //失敗操做 LAError errorCode = error.code; switch (errorCode) { case LAErrorAuthenticationFailed: { NSLog(@"受權失敗"); // -1 連續三次指紋識別錯誤 } break; case LAErrorUserCancel: // Authentication was canceled by user (e.g. tapped Cancel button) { NSLog(@"用戶取消驗證Touch ID"); // -2 在TouchID對話框中點擊了取消按鈕 } break; case LAErrorUserFallback: // Authentication was canceled, because the user tapped the fallback button (Enter Password) { NSLog(@"用戶選擇輸入密碼,切換主線程處理"); // -3 在TouchID對話框中點擊了輸入密碼按鈕 } break; case LAErrorSystemCancel: // Authentication was canceled by system (e.g. another application went to foreground) { NSLog(@"取消受權,如其餘應用切入"); // -4 TouchID對話框被系統取消,例如按下Home或者電源鍵 } break; case LAErrorPasscodeNotSet: // Authentication could not start, because passcode is not set on the device. { NSLog(@"設備系統未設置密碼"); // -5 } break; case LAErrorTouchIDNotAvailable: // Authentication could not start, because Touch ID is not available on the device { NSLog(@"設備未設置Touch ID"); // -6 } break; case LAErrorTouchIDNotEnrolled: // Authentication could not start, because Touch ID has no enrolled fingers { NSLog(@"用戶未錄入指紋"); // -7 } break; case LAErrorTouchIDLockout: //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 用戶連續屢次進行Touch ID驗證失敗,Touch ID被鎖,須要用戶輸入密碼解鎖,先Touch ID驗證密碼 { NSLog(@"Touch ID被鎖,須要用戶輸入密碼解鎖"); // -8 連續五次指紋識別錯誤,TouchID功能被鎖定,下一次須要輸入系統密碼 } break; case LAErrorAppCancel: // Authentication was canceled by application (e.g. invalidate was called while authentication was in progress) 如忽然來了電話,電話應用進入前臺,APP被掛起啦"); { NSLog(@"用戶不能控制狀況下APP被掛起"); // -9 } break; case LAErrorInvalidContext: // LAContext passed to this call has been previously invalidated. { NSLog(@"LAContext傳遞給這個調用以前已經失效"); // -10 } break; } }); } }); }];
指紋識別的彈窗的級別很是之高,高到離譜,通過驗證應用程序內部沒有比指紋識別的window的級別更高的UIWindowLevel,也就說了他是系統級的彈窗。須要注意的是,若是指紋彈窗顯示和消失應用程序會調用:
- (void)applicationWillResignActive:(UIApplication *)application; - (void)applicationDidBecomeActive:(UIApplication *)application;
因此應用程序內部沒法獲取。不知道越獄以後的手機可否獲取到,若是能獲取到,那就不可描述了,因此推薦各位看官沒什麼剛需不要越獄。
蘋果官方文檔解釋以下
This property returns a value only when the canEvaluatePolicy(:error:) method succeeds for a biometric policy or the evaluatePolicy(:localizedReason:reply:) method is called and a successful Touch ID authentication is performed. Otherwise, nil is returned.
The returned data is an opaque structure. It can be used to compare with other values returned by this property to determine whether the database of authorized fingerprints has been updated. However, the nature of the change cannot be determined from this data.
總結來講:
canEvaluatePolicy(_:error:)
或者evaluatePolicy(_:localizedReason:reply:)
方法驗證;成功後evaluatedPolicyDomainState屬性會返回一個 NSData 對象;不然返回 nil;根據上面的信息,咱們就能夠每次使用指紋的時候檢測指紋數據庫是否發生改變並做出相應的操做;下面是stackOverFlow 作的一個相應示例
從設備和系統判斷是不是支持TouchID,有點取巧
.h 文件
// // NSString+QDTouchID.h // Ewallet // // Created by 陳博文 on 16/10/25. // Copyright © 2016年 UCSMY. All rights reserved. // #import <Foundation/Foundation.h> @interface NSString (QDTouchID) + (BOOL)judueIPhonePlatformSupportTouchID; @end
. m 文件
//
// NSString+QDTouchID.m
// Ewallet
//
// Created by 陳博文 on 16/10/25. // Copyright © 2016年 UCSMY. All rights reserved. // #import "NSString+QDTouchID.h" #include <sys/sysctl.h> #define IOS8_OR_LATER ( [[[UIDevice currentDevice] systemVersion] compare:@"8.0"] != NSOrderedAscending ) #define IS_Phone UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPhone @implementation NSString (QDTouchID) //是不是iOS8.0以上的系統 //是不是5s以上的設備支持 + (NSString *)platform { size_t size; sysctlbyname("hw.machine", NULL, &size, NULL, 0); char *machine = malloc(size); sysctlbyname("hw.machine", machine, &size, NULL, 0); NSString *platform = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding]; free(machine); return platform; } // 判斷是否支持TouchID,只判斷手機端,ipad端咱們不支持 + (BOOL)judueIPhonePlatformSupportTouchID { /* if ([platform isEqualToString:@"iPhone1,1"]) return @"iPhone1G GSM"; if ([platform isEqualToString:@"iPhone1,2"]) return @"iPhone3G GSM"; if ([platform isEqualToString:@"iPhone2,1"]) return @"iPhone3GS GSM"; if ([platform isEqualToString:@"iPhone3,1"]) return @"iPhone4 GSM"; if ([platform isEqualToString:@"iPhone3,3"]) return @"iPhone4 CDMA"; if ([platform isEqualToString:@"iPhone4,1"]) return @"iPhone4S"; if ([platform isEqualToString:@"iPhone5,1"]) return @"iPhone5"; if ([platform isEqualToString:@"iPhone5,2"]) return @"iPhone5"; if ([platform isEqualToString:@"iPhone5,3"]) return @"iPhone 5c (A1456/A1532)"; if ([platform isEqualToString:@"iPhone5,4"]) return @"iPhone 5c (A1507/A1516/A1526/A1529)"; if ([platform isEqualToString:@"iPhone6,1"]) return @"iPhone 5s (A1453/A1533)"; if ([platform isEqualToString:@"iPhone6,2"]) return @"iPhone 5s (A1457/A1518/A1528/A1530)"; */ if(IS_Phone) { if([self platform].length > 6 ) { NSString * numberPlatformStr = [[self platform] substringWithRange:NSMakeRange(6, 1)]; NSInteger numberPlatform = [numberPlatformStr integerValue]; // 是不是5s以上的設備 if(numberPlatform > 5) { return YES; } else { return NO; } } else { return NO; } } else { // 咱們不支持iPad 設備 return NO; } } @end