在不设置密码的情况下确定 iOS 设备是否支持 TouchID
Determine if an iOS device supports TouchID without setting passcode
我目前正在开发一个 iOS 应用程序,使用户能够使用 TouchID 登录应用程序,但首先他们必须先在应用程序内设置密码。问题是,要显示启用 TouchID 登录的设置密码选项,我需要检测 iOS 设备是否支持 TouchID。
使用 LAContext 和 canEvaluatePolicy(就像这里的答案 If Device Supports Touch ID),我能够确定当前设备是否支持 TouchID 如果用户已经在他们的 iOS 设备。这是我的代码片段(我使用的是 Xamarin,所以它在 C# 中):
static bool DeviceSupportsTouchID ()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var context = new LAContext();
NSError authError;
bool touchIDSetOnDevice = context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out authError);
return (touchIDSetOnDevice || (LAStatus) Convert.ToInt16(authError.Code) != LAStatus.TouchIDNotAvailable);
}
return false;
}
如果用户没有设置设备密码,authError 只会return“PasscodeNotSet”错误设备是否真的支持 TouchID。
如果用户的设备支持 TouchID,我希望始终在我的应用程序中显示 TouchID 选项,无论用户是否在他们的设备上设置了密码(我只会警告用户先在他们的设备上设置密码) .反之亦然,如果用户的设备不支持 TouchID,我显然不想在我的应用程序中显示 TouchID 选项。
所以我的问题是,有没有一种好方法可以始终如一地确定 iOS 设备是否支持 TouchID,而不管用户是否在他们的设备上设置了密码?
我能想到的唯一解决方法是确定设备的架构(已在 Determine if iOS device is 32- or 64-bit 中回答),因为 TouchID 仅在具有 64 位架构的设备上受支持。不过,我正在寻找是否有更好的方法来做到这一点。
检测TouchID是否可用的正确方法:
BOOL hasTouchID = NO;
// if the LAContext class is available
if ([LAContext class]) {
LAContext *context = [LAContext new];
NSError *error = nil;
hasTouchId = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
}
抱歉,它在 Objective-C 中,您可能需要将它翻译成 C#。
请不要检查系统版本,只检查class或方法是否可用。
总结一下下面的讨论,当用户没有在他们的设备上设置密码时,暂时无法确定设备是否真正支持 TouchID。
我已经在 Apple bug reporter 上报告了这个 TouchID 漏洞。那些想要关注这个问题的人可以在此处的 Open Radar 上看到它:http://www.openradar.me/20342024
感谢@rckoenes 的输入:)
编辑
原来有人已经报告了类似的问题 (#18364575)。以下是 Apple 对此问题的回复:
“工程部门已根据以下信息确定此问题的行为符合预期:
如果未设置密码,您将无法检测到 Touch ID 存在。设置密码后,canEvaluatePolicy 最终将 return LAErrorTouchIDNotAvailable 或 LAErrorTouchIdNotEnrolled 并且您将能够检测到 Touch ID presence/state.
如果用户使用 Touch ID 在 phone 上禁用密码,他们知道他们将无法使用 Touch ID,因此应用程序不需要检测 Touch ID 的存在或宣传基于 Touch ID 的功能。 "
所以.....Apple 的最终答案是否。 :(
注意:报告此问题的人提出了类似的 Whosebug 问题 -> iOS8 check if device has Touch ID
(想知道为什么尽管我进行了广泛的搜索,但我之前没有找到这个问题......)
这里有一个有点乏味的方法来确定设备是否有物理触摸 ID 传感器。
+ (BOOL)isTouchIDExist {
if(![LAContext class]) //Since this mandotory class is not there, that means there is no physical touch id.
return false;
//Get the current device model name
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *model = malloc(size);
sysctlbyname("hw.machine", model, &size, NULL, 0);
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
//Devices that does not support touch id
NSArray *deviceModelsWithoutTouchID = [[NSArray alloc]
initWithObjects:
@"iPhone1,1", //iPhone
@"iPhone1,2", //iPhone 3G
@"iPhone2,1", //iPhone 3GS
@"iPhone3,1", //iPhone 4
@"iPhone3,2",
@"iPhone3,3",
@"iPhone4,1", //iPhone 4S
@"iPhone5,1", //iPhone 5
@"iPhone5,2",
@"iPhone5,3", //iPhone 5C
@"iPhone5,4",
@"iPod1,1", //iPod
@"iPod2,1",
@"iPod3,1",
@"iPod4,1",
@"iPod5,1",
@"iPod7,1",
@"iPad1,1", //iPad
@"iPad2,1", //iPad 2
@"iPad2,2",
@"iPad2,3",
@"iPad2,4",// iPad mini 1G
@"iPad2,5",
@"iPad2,5",
@"iPad2,7",
@"iPad3,1", //iPad 3
@"iPad3,2",
@"iPad3,3",
@"iPad3,4", //iPad 4
@"iPad3,5",
@"iPad3,6",
@"iPad4,1", //iPad Air
@"iPad4,2",
@"iPad4,3",
@"iPad4,4", //iPad mini 2
@"iPad4,5",
@"iPad4,6",
@"iPad4,7",
nil];
return ![deviceModelsWithoutTouchID containsObject:deviceModel];
}
参考:
https://www.theiphonewiki.com/wiki/Models
https://en.wikipedia.org/wiki/IOS
我知道这是去年的问题,但是这个解决方案没有满足您的需求? (Swift 代码)
if #available(iOS 8.0, *) {
var error: NSError?
let hasTouchID = LAContext().canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)
//Show the touch id option if the device has touch id hardware feature (even if the passcode is not set or touch id is not enrolled)
if(hasTouchID || (error?.code != LAError.TouchIDNotAvailable.rawValue)) {
touchIDContentView.hidden = false
}
}
然后,当用户按下按钮使用touch id登录时:
@IBAction func loginWithTouchId() {
let context = LAContext()
var error: NSError?
let reasonString = "Log in with Touch ID"
if (context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)) {
[context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
//Has touch id. Treat the success boolean
})]
} else {
//Then, if the user has touch id but is not enrolled or the passcode is not set, show a alert message
switch error!.code{
case LAError.TouchIDNotEnrolled.rawValue:
//Show alert message to inform that touch id is not enrolled
break
case LAError.PasscodeNotSet.rawValue:
//Show alert message to inform that passcode is not set
break
default:
// The LAError.TouchIDNotAvailable case.
// Will not catch here, because if not available, the option will not visible
}
}
}
希望对您有所帮助!
对于Objective C
它在所有设备上运行良好,无需检查设备版本。
- (void)canAuthenticatedByTouchID{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = touchIDRequestReason;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
}else{
switch (authError.code) {
case kLAErrorTouchIDNotAvailable:
[labelNotSupportTouchID setHidden:NO];
[switchBtn setHidden:YES];
[labelEnableTouchid setHidden:YES];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self showAlertMessage:@"EyeCheck Pro" message:@"Device does not support Touch ID Service."];
});
break;
}
}
}
对于 iOS 11+,您可以使用 biometryType: LABiometryType
或 LAContext
。更多来自 Apple 文档:
/// Indicates the type of the biometry supported by the device.
///
/// @discussion This property is set only when canEvaluatePolicy succeeds for a biometric policy.
/// The default value is LABiometryTypeNone.
@available(iOS 11.0, *)
open var biometryType: LABiometryType { get }
@available(iOS 11.0, *)
public enum LABiometryType : Int {
/// The device does not support biometry.
@available(iOS 11.2, *)
case none
/// The device does not support biometry.
@available(iOS, introduced: 11.0, deprecated: 11.2, renamed: "LABiometryType.none")
public static var LABiometryNone: LABiometryType { get }
/// The device supports Touch ID.
case touchID
/// The device supports Face ID.
case faceID
}
您可以通过以下方式识别设备是否支持 Touch ID 或 Face ID
open class LocalAuth: NSObject {
public static let shared = LocalAuth()
private override init() {}
var laContext = LAContext()
func canAuthenticate() -> Bool {
var error: NSError?
let hasTouchId = laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
return hasTouchId
}
func hasTouchId() -> Bool {
if canAuthenticate() && laContext.biometryType == .touchID {
return true
}
return false
}
func hasFaceId() -> Bool {
if canAuthenticate() && laContext.biometryType == .faceID {
return true
}
return false
}
}
下面是above-shared代码的用法
if LocalAuth.shared.hasTouchId() {
print("Has Touch Id")
} else if LocalAuth.shared.hasFaceId() {
print("Has Face Id")
} else {
print("Device does not have Biometric Authentication Method")
}
对于iOS 11+,对于上下文错误,您可以检查kLAErrorBiometryNotAvailable
我目前正在开发一个 iOS 应用程序,使用户能够使用 TouchID 登录应用程序,但首先他们必须先在应用程序内设置密码。问题是,要显示启用 TouchID 登录的设置密码选项,我需要检测 iOS 设备是否支持 TouchID。
使用 LAContext 和 canEvaluatePolicy(就像这里的答案 If Device Supports Touch ID),我能够确定当前设备是否支持 TouchID 如果用户已经在他们的 iOS 设备。这是我的代码片段(我使用的是 Xamarin,所以它在 C# 中):
static bool DeviceSupportsTouchID ()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var context = new LAContext();
NSError authError;
bool touchIDSetOnDevice = context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out authError);
return (touchIDSetOnDevice || (LAStatus) Convert.ToInt16(authError.Code) != LAStatus.TouchIDNotAvailable);
}
return false;
}
如果用户没有设置设备密码,authError 只会return“PasscodeNotSet”错误设备是否真的支持 TouchID。
如果用户的设备支持 TouchID,我希望始终在我的应用程序中显示 TouchID 选项,无论用户是否在他们的设备上设置了密码(我只会警告用户先在他们的设备上设置密码) .反之亦然,如果用户的设备不支持 TouchID,我显然不想在我的应用程序中显示 TouchID 选项。
所以我的问题是,有没有一种好方法可以始终如一地确定 iOS 设备是否支持 TouchID,而不管用户是否在他们的设备上设置了密码?
我能想到的唯一解决方法是确定设备的架构(已在 Determine if iOS device is 32- or 64-bit 中回答),因为 TouchID 仅在具有 64 位架构的设备上受支持。不过,我正在寻找是否有更好的方法来做到这一点。
检测TouchID是否可用的正确方法:
BOOL hasTouchID = NO;
// if the LAContext class is available
if ([LAContext class]) {
LAContext *context = [LAContext new];
NSError *error = nil;
hasTouchId = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
}
抱歉,它在 Objective-C 中,您可能需要将它翻译成 C#。
请不要检查系统版本,只检查class或方法是否可用。
总结一下下面的讨论,当用户没有在他们的设备上设置密码时,暂时无法确定设备是否真正支持 TouchID。
我已经在 Apple bug reporter 上报告了这个 TouchID 漏洞。那些想要关注这个问题的人可以在此处的 Open Radar 上看到它:http://www.openradar.me/20342024
感谢@rckoenes 的输入:)
编辑
原来有人已经报告了类似的问题 (#18364575)。以下是 Apple 对此问题的回复:
“工程部门已根据以下信息确定此问题的行为符合预期:
如果未设置密码,您将无法检测到 Touch ID 存在。设置密码后,canEvaluatePolicy 最终将 return LAErrorTouchIDNotAvailable 或 LAErrorTouchIdNotEnrolled 并且您将能够检测到 Touch ID presence/state.
如果用户使用 Touch ID 在 phone 上禁用密码,他们知道他们将无法使用 Touch ID,因此应用程序不需要检测 Touch ID 的存在或宣传基于 Touch ID 的功能。 "
所以.....Apple 的最终答案是否。 :(
注意:报告此问题的人提出了类似的 Whosebug 问题 -> iOS8 check if device has Touch ID (想知道为什么尽管我进行了广泛的搜索,但我之前没有找到这个问题......)
这里有一个有点乏味的方法来确定设备是否有物理触摸 ID 传感器。
+ (BOOL)isTouchIDExist {
if(![LAContext class]) //Since this mandotory class is not there, that means there is no physical touch id.
return false;
//Get the current device model name
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *model = malloc(size);
sysctlbyname("hw.machine", model, &size, NULL, 0);
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
//Devices that does not support touch id
NSArray *deviceModelsWithoutTouchID = [[NSArray alloc]
initWithObjects:
@"iPhone1,1", //iPhone
@"iPhone1,2", //iPhone 3G
@"iPhone2,1", //iPhone 3GS
@"iPhone3,1", //iPhone 4
@"iPhone3,2",
@"iPhone3,3",
@"iPhone4,1", //iPhone 4S
@"iPhone5,1", //iPhone 5
@"iPhone5,2",
@"iPhone5,3", //iPhone 5C
@"iPhone5,4",
@"iPod1,1", //iPod
@"iPod2,1",
@"iPod3,1",
@"iPod4,1",
@"iPod5,1",
@"iPod7,1",
@"iPad1,1", //iPad
@"iPad2,1", //iPad 2
@"iPad2,2",
@"iPad2,3",
@"iPad2,4",// iPad mini 1G
@"iPad2,5",
@"iPad2,5",
@"iPad2,7",
@"iPad3,1", //iPad 3
@"iPad3,2",
@"iPad3,3",
@"iPad3,4", //iPad 4
@"iPad3,5",
@"iPad3,6",
@"iPad4,1", //iPad Air
@"iPad4,2",
@"iPad4,3",
@"iPad4,4", //iPad mini 2
@"iPad4,5",
@"iPad4,6",
@"iPad4,7",
nil];
return ![deviceModelsWithoutTouchID containsObject:deviceModel];
}
参考: https://www.theiphonewiki.com/wiki/Models https://en.wikipedia.org/wiki/IOS
我知道这是去年的问题,但是这个解决方案没有满足您的需求? (Swift 代码)
if #available(iOS 8.0, *) {
var error: NSError?
let hasTouchID = LAContext().canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)
//Show the touch id option if the device has touch id hardware feature (even if the passcode is not set or touch id is not enrolled)
if(hasTouchID || (error?.code != LAError.TouchIDNotAvailable.rawValue)) {
touchIDContentView.hidden = false
}
}
然后,当用户按下按钮使用touch id登录时:
@IBAction func loginWithTouchId() {
let context = LAContext()
var error: NSError?
let reasonString = "Log in with Touch ID"
if (context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error)) {
[context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success: Bool, evalPolicyError: NSError?) -> Void in
//Has touch id. Treat the success boolean
})]
} else {
//Then, if the user has touch id but is not enrolled or the passcode is not set, show a alert message
switch error!.code{
case LAError.TouchIDNotEnrolled.rawValue:
//Show alert message to inform that touch id is not enrolled
break
case LAError.PasscodeNotSet.rawValue:
//Show alert message to inform that passcode is not set
break
default:
// The LAError.TouchIDNotAvailable case.
// Will not catch here, because if not available, the option will not visible
}
}
}
希望对您有所帮助!
对于Objective C
它在所有设备上运行良好,无需检查设备版本。
- (void)canAuthenticatedByTouchID{
LAContext *myContext = [[LAContext alloc] init];
NSError *authError = nil;
NSString *myLocalizedReasonString = touchIDRequestReason;
if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&authError]) {
}else{
switch (authError.code) {
case kLAErrorTouchIDNotAvailable:
[labelNotSupportTouchID setHidden:NO];
[switchBtn setHidden:YES];
[labelEnableTouchid setHidden:YES];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self showAlertMessage:@"EyeCheck Pro" message:@"Device does not support Touch ID Service."];
});
break;
}
}
}
对于 iOS 11+,您可以使用 biometryType: LABiometryType
或 LAContext
。更多来自 Apple 文档:
/// Indicates the type of the biometry supported by the device.
///
/// @discussion This property is set only when canEvaluatePolicy succeeds for a biometric policy.
/// The default value is LABiometryTypeNone.
@available(iOS 11.0, *)
open var biometryType: LABiometryType { get }
@available(iOS 11.0, *)
public enum LABiometryType : Int {
/// The device does not support biometry.
@available(iOS 11.2, *)
case none
/// The device does not support biometry.
@available(iOS, introduced: 11.0, deprecated: 11.2, renamed: "LABiometryType.none")
public static var LABiometryNone: LABiometryType { get }
/// The device supports Touch ID.
case touchID
/// The device supports Face ID.
case faceID
}
您可以通过以下方式识别设备是否支持 Touch ID 或 Face ID
open class LocalAuth: NSObject {
public static let shared = LocalAuth()
private override init() {}
var laContext = LAContext()
func canAuthenticate() -> Bool {
var error: NSError?
let hasTouchId = laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
return hasTouchId
}
func hasTouchId() -> Bool {
if canAuthenticate() && laContext.biometryType == .touchID {
return true
}
return false
}
func hasFaceId() -> Bool {
if canAuthenticate() && laContext.biometryType == .faceID {
return true
}
return false
}
}
下面是above-shared代码的用法
if LocalAuth.shared.hasTouchId() {
print("Has Touch Id")
} else if LocalAuth.shared.hasFaceId() {
print("Has Face Id")
} else {
print("Device does not have Biometric Authentication Method")
}
对于iOS 11+,对于上下文错误,您可以检查kLAErrorBiometryNotAvailable