Как программно проверить поддержку "Face Id" и "Touch Id"
Я интегрировал LocalAuthentication в целях безопасности своего приложения, которое поддерживало поддержку на основе Touch Id. Но теперь Apple недавно добавила аутентификацию на основе "Face Id".
Как я могу проверить, какой тип аутентификации поддерживается устройством. Touch Id или Face Id?
15 ответов
С Xcode 9, Посмотрите на LocalAuthentication -> LAContext -> LABiometryType.
LABiometryType - это перечисление со значениями, как на прикрепленном изображении
Вы можете проверить, какой тип аутентификации поддерживается устройством между Touch ID и FaceID или нет.
Редактировать:
Apple обновила значения для этого перечисления LABiometryType. никто не устарел сейчас.
Я изо всех сил пытался заставить это работать, и обнаружил, что мне нужно использовать один экземпляр LAContext и нужно вызвать LAContextInstance.canEvaluatePolicy (.deviceOwnerAuthenticationWithBiometrics, error: nil) перед получением biometryType. Вот мой окончательный код с поддержкой более старых версий iOS:
static func biometricType() -> BiometricType {
let authContext = LAContext()
if #available(iOS 11, *) {
let _ = authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
switch(authContext.biometryType) {
case .none:
return .none
case .touchID:
return .touch
case .faceID:
return .face
}
} else {
return authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touch : .none
}
}
enum BiometricType {
case none
case touch
case face
}
Как я большой поклонник расширения. Я формулирую этот ответ немного по-другому. Essense то же самое. Это добавочное расширение.
import LocalAuthentication
extension LAContext {
enum BiometricType: String {
case none
case touchID
case faceID
}
var biometricType: BiometricType {
var error: NSError?
guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
// Capture these recoverable error thru Crashlytics
return .none
}
if #available(iOS 11.0, *) {
switch self.biometryType {
case .none:
return .none
case .touchID:
return .touchID
case .faceID:
return .faceID
}
} else {
return self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
}
}
}
Используйте как это:
var currentType = LAContext().biometricType
Face ID доступен с iOS 11, а iPhone X поставляется с iOS 11 по умолчанию. В платформе LocalAuth они добавили свойство 'biometryType', которое может дать вам возможность определить, доступен ли Face ID на устройстве.
/// checks if face id is avaiable on device
func faceIDAvailable() -> Bool {
if #available(iOS 11.0, *) {
let context = LAContext()
return (context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthentication, error: nil) && context.biometryType == .faceID)
}
return false
}
Я создал одноэлементный класс для локальной аутентификации, так как он помогает инициализировать экземпляр один раз, используя static
свойство для всего приложения.
import Foundation
import LocalAuthentication
public class LocalAuthManager: NSObject {
public static let shared = LocalAuthManager()
private let context = LAContext()
private let reason = "Your Request Message"
private var error: NSError?
enum BiometricType: String {
case none
case touchID
case faceID
}
private override init() {
}
// check type of local authentication device currently support
var biometricType: BiometricType {
guard self.context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
return .none
}
if #available(iOS 11.0, *) {
switch context.biometryType {
case .none:
return .none
case .touchID:
return .touchID
case .faceID:
return .faceID
}
} else {
return self.context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) ? .touchID : .none
}
}
}
Реализация:
func checkAuth() {
let authType = LocalAuthManager.shared.biometricType
switch authType {
case .none:
print("Device not registered with TouchID/FaceID")
case .touchID:
print("Device support TouchID")
case .faceID:
print("Device support FaceID")
}
}
Цель C:)
/** Only interesting devices are enumerated here. To change view constraints depending
on screen height. Or the top notch for iPhone X
*/
typedef NS_ENUM(NSUInteger, BPDeviceType) {
BPDeviceTypeUnknown,
BPDeviceTypeiPhone4,
BPDeviceTypeiPhone5,
BPDeviceTypeiPhone6,
BPDeviceTypeiPhone6Plus,
BPDeviceTypeiPhone7,
BPDeviceTypeiPhone7Plus,
BPDeviceTypeiPhoneX,
BPDeviceTypeiPad
};
+ (BPDeviceType)getDeviceType {
double screenHeight = [[UIScreen mainScreen] bounds].size.height;
if(UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad)
{
return BPDeviceTypeiPad;
} else if (UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone)
{
if (@available(iOS 11, *)) {
UIEdgeInsets insets = [UIApplication sharedApplication].delegate.window.safeAreaInsets;
if (insets.top > 0) {
return BPDeviceTypeiPhoneX;
}
}
if(screenHeight == 480) {
return BPDeviceTypeiPhone4;
} else if (screenHeight == 568) {
return BPDeviceTypeiPhone5;
} else if (screenHeight == 667) {
return BPDeviceTypeiPhone6;
} else if (screenHeight == 736) {
return BPDeviceTypeiPhone6Plus;
}
}
return BPDeviceTypeUnknown;
}
+ (BOOL) isBiometricIDAvailable {
if (![LAContext class]) return NO;
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
NSLog(@"%@", [authError localizedDescription]);
return NO;
}
return YES;
}
+ (BOOL) isTouchIDAvailable {
if (![LAContext class]) return NO;
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
NSLog(@"%@", [authError localizedDescription]);
return NO;
// if (authError.code == LAErrorTouchIDNotAvailable) {}
}
if (@available(iOS 11.0, *)) {
if (myContext.biometryType == LABiometryTypeTouchID){
return YES;
} else {
return NO;
}
} else {
return YES;
}
}
+ (BOOL) supportFaceID {
return [BPDeviceInfo getDeviceType] == BPDeviceTypeiPhoneX;
}
+ (BOOL) isFaceIDAvailable {
if (![LAContext class]) return NO;
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (![myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
NSLog(@"%@", [authError localizedDescription]);
return NO;
}
if (@available(iOS 11.0, *)) {
if (myContext.biometryType == LABiometryTypeFaceID){
return YES;
} else {
return NO;
}
} else {
return NO;
}
}
Вот мой "вспомогательный класс", он также включает в себя пароль
enum BiometryType: String {
case none = "None"
case faceID = "Face ID"
case touchID = "Touch ID"
case passcode = "Passcode"
}
var biometryType: BiometryType {
let myContext = LAContext()
let hasAuthenticationBiometrics = myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
let hasAuthentication = myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
if #available(iOS 11.0, *) {
if hasAuthentication {
if hasAuthenticationBiometrics {
switch myContext.biometryType {
case .none: return .none
case .faceID: return .faceID
case .touchID: return .touchID
}
} else {
return .passcode
}
} else {
return .none
}
} else {
if hasAuthentication {
if hasAuthenticationBiometrics {
return .touchID
} else {
return .passcode
}
} else {
return .none
}
}
}
Вот еще один способ через свойство (например, в вашем экземпляре доступа).
import LocalAuthentication
enum BiometricType {
case none
case touchID
case faceID
}
var biometricType: BiometricType {
get {
let context = LAContext()
var error: NSError?
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
print(error?.localizedDescription ?? "")
return .none
}
if #available(iOS 11.0, *) {
switch context.biometryType {
case .none:
return .none
case .typeTouchID:
return .touchID
case .typeFaceID:
return .faceID
}
} else {
return .touchID
}
}
}
-(BOOL)faceIDAvailable {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (@available(iOS 11.0, *)) {
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError] && myContext.biometryType == LABiometryTypeFaceID) {
return true;
}
}
return false;
}
-(BOOL)touchIDAvailable {
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
if (@available(iOS 11.0, *)) {
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:&authError] && myContext.biometryType == LABiometryTypeTouchID) {
return true;
}
}
return false;
}
You NEED to add @unknown to the cases!!!
In newer Versions(Xcode 13...) you may have to specify "unknown"
#warning("Handle new Biometric type") doesn't return a value
import LocalAuthentication
class BiometricType{
static func biometricType() -> BiometricType {
let authContext = LAContext()
if #available(iOS 11, *) {
let _ = authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
switch(authContext.biometryType) {
case .none:
return .none
case .touchID:
return .touch
case .faceID:
return .face
@unknown default:
return .unknown
}
} else {
return authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touch : .none
}
}
enum BiometricType {
case none
case touch
case face
case unknown
}
}
Пожалуйста, обратитесь к образцу кода Apple, предоставленному для "Входа пользователя в ваше приложение с помощью Face ID или Touch ID", который поможет вам легко понять аутентификацию.
Ссылка на образец кода Apple - https://docs-assets.developer.apple.com/published/fbfd13f4d9/LoggingAUserIntoYourAppWithFaceIDOrTouchID.zip
Прочтите подробное объяснение примера кода по ссылке ниже. https://developer.apple.com/documentation/localauthentication/logging_a_user_into_your_app_with_face_id_or_touch_id
Обновление для Swift 5, для переключения потока требуется условие по умолчанию.
import Foundation
import LocalAuthentication
extension LAContext {
enum BiometricType: String {
case none
case touchID
case faceID
}
var biometricType: BiometricType {
var error: NSError?
guard self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
// Capture these recoverable error through fabric
return .none
}
if #available(iOS 11.0, *) {
switch self.biometryType {
case .touchID:
return .touchID
case .faceID:
return .faceID
default:
return .none
}
}
return self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
}
}
тестовый пример находится ниже
// need to import LocalAuthentication in the calling file
// import LocalAuthentication
let currentType = LAContext().biometricType
print("biometry type > \(currentType)")
// biometry type > touchID
Если вы хотите протестировать в симуляторе, вам необходимо зарегистрировать touchId/faceId.
Симулятор> Оборудование> Touch ID/Face ID > Зарегистрирован.
От расширения @Markicevic, но игнорируя случаи, когда пользователь не зарегистрирован и т.д.
extension LAContext {
enum BiometricType: String {
case none = ""
case touchID = "Touch ID"
case faceID = "Face ID"
}
static var biometricType: BiometricType {
var error: NSError?
let context = LAContext()
_ = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if error?.code == LAError.Code.touchIDNotAvailable.rawValue {
return .none
}
if #available(iOS 11.0, *) {
switch context.biometryType {
case .none:
return .none
case .touchID:
return .touchID
case .faceID:
return .faceID
}
} else {
return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
}
}
}
Этот код создается без предупреждений на XCode 9.2
-9.4
(см. комментарии для 9.1
):
@objc let biometricsAuthPolicy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
@objc func supportsFaceID() -> Bool {
if #available(iOS 11.0, *) {
return biometryType() == .faceID // return biometryType() == .typeFaceID for Xcode 9.1
}
return false
}
@objc func supportsTouchID() -> Bool {
if #available(iOS 11.0, *) {
return biometryType() == .touchID // return biometryType() == .typeTouchID for Xcode 9.1
}
let context = LAContext()
return context.canEvaluatePolicy(biometricsAuthPolicy, error: nil)
}
@objc @available(iOS 11.0, *)
func biometryType() -> LABiometryType {
var error: NSError?
let context = LAContext()
guard context.canEvaluatePolicy(biometricsAuthPolicy, error: &error) else {
if #available(iOS 11.2, *) {
return .none
}
return LABiometryType.LABiometryNone // return LABiometryType.none for Xcode 9.1
}
return context.biometryType
}
Начал тестирование некоторых новых приложений на 12 Pro и понял, что мои опубликованные приложения имеют только Touch ID, а не Face ID.
Я пришел сюда и увидел все это, поэтому я начал пытаться изменить свой код Touch ID, но все, что мне нужно было сделать, это добавить ключ конфиденциальности в info.plist.
Информация о недвижимости
Затем прокрутите вниз до: Конфиденциальность - Описание использования Face ID, (Тип: Строка), (Значение: ДА)
Слишком просто😀