iOS8如何检查设备是否支持Touch ID

17

LAContext有一个方法用于检查设备是否可以评估Touch ID,并提供错误消息。

问题在于系统在两种情况下都会给出相同的错误消息“LAErrorPasscodeNotSet”:

1)如果用户有Touch ID,但在设置中将其关闭(使用iOS8的iPhone 5s)

2)如果设备没有Touch ID(使用iOS8的iPad)

问:如何检查设备是否支持Touch ID,但尚未在设置中打开它?

更新:

已向Apple创建了关于此错误的工单(ID# 18364575),并收到答复:

"根据以下信息,工程师已确定此问题的行为符合预期:

如果未设置密码,您将无法检测到Touch ID的存在。一旦设置了密码,canEvaluatePolicy最终将返回LAErrorTouchIDNotAvailable或LAErrorTouchIdNotEnrolled,您将能够检测到Touch ID的存在/状态。"

如果用户已禁用带有Touch ID的手机的密码,他们知道他们将无法使用Touch ID,因此应用程序不需要检测Touch ID的存在或推广基于Touch ID的功能。"


工程师的回复让我想起了请求位置权限时的建议(如果用户关闭了或未授权应用程序,则不要再次询问)。问题在于,它假定用户熟悉功能和设置,即他们总是知道在设置中重新打开它们。然而,根据我的经验,这是一厢情愿的想法... 用户会忘记这些事情,这是可以理解的。 - stephent
2
看起来苹果已经修复了这个问题:iOS 8.1 iPad 3(无触摸)- LAErrorPasscodeNotSet,iOS 8.4 iPad 3(无触摸)- LAErrorTouchIDNotAvailable。 - Uladzimir
只是出于好奇,关于您的应用程序设计:当用户明确禁用TouchID并且对您的应用程序不可用时,为什么您会关心设备是否具有TouchID硬件支持? - Nicolas Miari
5个回答

7
也许你可以编写自己的方法来检查你正在运行的设备,因为如果返回的错误相同,很难确定Touch ID是否受支持。我会采用以下方法:
int sysctlbyname(const char *, void *, size_t *, void *, size_t);

- (NSString *)getSysInfoByName:(char *)typeSpecifier
{
    size_t size;
    sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);

    char *answer = malloc(size);
    sysctlbyname(typeSpecifier, answer, &size, NULL, 0);

    NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];

    free(answer);
    return results;
}

- (NSString *)modelIdentifier
{
    return [self getSysInfoByName:"hw.machine"];
}

获得了型号标识符后,我只需检查型号标识符是否等于支持Touch ID的其中一个型号:

- (BOOL)hasTouchID
{
    NSArray *touchIDModels = @[ @"iPhone6,1", @"iPhone6,2", @"iPhone7,1", @"iPhone7,2", @"iPad5,3", @"iPad5,4", @"iPad4,7", @"iPad4,8", @"iPad4,9" ];

    NSString *model = [self modelIdentifier];

    return [touchIDModels containsObject:model];
}

这个数组包含所有支持Touch ID的型号ID,它们是:

  • iPhone 5s
  • iPhone 6
  • iPhone 6+
  • iPad Air 2
  • iPad Mini 3

这种方法唯一的缺点是,一旦有新设备发布并支持Touch ID,模型数组就必须手动更新。


1
谢谢,Legoless。 我建议了这样的解决方案。但正如你在消息末尾指出的那样 - 这是一个临时解决方案,所以当新设备出现时,我应该尽快更新它。 我希望有另一种方法,可以避免手动更新。 - 1ce
我们可以轻易地假设所有新的iPhone也将拥有Touch ID,因此我们可以添加一些逻辑,如果iPhone的型号高于6.x,则可以返回YES。只需要小心iPad是否具有Touch ID,但每年更新一次应该不是问题 - 除了你已经在为新的iOS更新应用程序了。只需要小心不要忘记它。 - Legoless
我们知道新设备将支持Touch ID,因此我们只需检查它是否为iPhone 4S和5,然后我们就知道它不支持Touch ID。因为iPhone 4或更低版本无法升级到iOS 8,如果不是iOS8,则错误将为空。这样就不需要在宣布新设备时更新应用程序。 - Paragon
这也是正确的Paragon。实际上检查这一点会更有意义。无论如何,iOS 7都不支持Touch ID,但我们仍然必须知道确切的问题是什么。它是“这个设备有Touch ID吗?”还是“我可以使用Touch ID吗?”。然而,并不确定所有新设备将来都会有Touch ID。 - Legoless
1
我最终使用了相同的解决方案,即使它不是未来可靠且不够优雅。相反的解决方法也不是未来可靠的,因为苹果可能会发布更多没有TouchID的设备(C系列、iPod touch等)。希望他们能引入更好的方法来检测这个问题。 - GuillermoMP

1
在这里,您可以检查Touch-ID和Face-ID(使用iOS 11+)。
使用LAContextbiometryType属性来检查和评估可用的生物识别策略。(对于密码验证,当生物识别失败时,请使用:LAPolicyDeviceOwnerAuthentication
尝试一下并看看: Objective-C:
LAContext *laContext = [[LAContext alloc] init];

NSError *error;


// For a passcode authentication , when biometric fails, use: LAPolicyDeviceOwnerAuthentication
//if ([laContext canEvaluatePolicy: LAPolicyDeviceOwnerAuthentication error:&error]) {
if ([laContext canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) {    
    if (error != NULL) {
        // handle error
    } else {

        if (@available(iOS 11, *)) {
            if (laContext.biometryType == LABiometryTypeFaceID) {
                //localizedReason = "Unlock using Face ID"
                NSLog(@"FaceId support");
            } else if (laContext.biometryType == LABiometryTypeTouchID) {
                //localizedReason = "Unlock using Touch ID"
                NSLog(@"TouchId support");
            } else {
                //localizedReason = "Unlock using Application Passcode"
                NSLog(@"No biometric support or Denied biometric support");
            }
        } else {
            // Fallback on earlier versions
        }


        [laContext evaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:@"Test Reason" reply:^(BOOL success, NSError * _Nullable error) {

            if (error != NULL) {
                // handle error
            } else if (success) {
                // handle success response
            } else {
                // handle false response
            }
        }];
    }
}

Swift:

let laContext = LAContext()
var error: NSError?
let biometricsPolicy = LAPolicy.deviceOwnerAuthentication //LAPolicy.deviceOwnerAuthenticationWithBiometrics

if laContext.isCredentialSet(LACredentialType.applicationPassword) {
    print("Passsword is set")
}

let localizedFallbackTitle = "Unlock Using Device Passcode"
let localizedCancelTitle = "Use Application Passcode"
if (laContext.canEvaluatePolicy(biometricsPolicy, error: &error)) {

    if let laError = error {
        print("laError - \(laError)")
        return
    }



    //print("biometricsPolicy - \(biometricsPolicy.rawValue)")

    UINavigationBar.appearance().tintColor = UIColor.red


    var localizedReason = "My Reason to be displayed on face id prompt"
    if #available(iOS 11.0, *) {
        if (laContext.biometryType == LABiometryType.faceID) {
            //localizedReason = "Unlock using Face ID"
            print("FaceId support")
        } else if (laContext.biometryType == LABiometryType.touchID) {
            //localizedReason = "Unlock using Touch ID"
            print("TouchId support")
        } else {
            //localizedReason = "Unlock using Application Passcode"
            print("No Biometric support")
        }
    } else {
        // Fallback on earlier versions
    }

    laContext.localizedFallbackTitle = localizedFallbackTitle
    laContext.localizedCancelTitle = localizedCancelTitle
    //laContext.localizedReason = "test loc reason"
    laContext.evaluatePolicy(biometricsPolicy, localizedReason: localizedReason, reply: { (isSuccess, error) in

        DispatchQueue.main.async(execute: {

            if let laError = error {
                print("laError - \(laError)")
            } else {
                if isSuccess {
                    print("sucess")
                } else {
                    print("failure")
                }
            }

        })
    })
}

1
在Swift 3中
fileprivate func deviceSupportsTouchId(success: @escaping () -> (), failure: @escaping (NSError) -> ()) {
    let context = LAContext()
    var authError: NSError?
    let touchIdSetOnDevice = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &authError)

    if touchIdSetOnDevice {
        DispatchQueue.main.async {
             success()
        }
    }
    else {
        DispatchQueue.main.async {
             failure(error!)
        }
    }
}

deviceSupportsTouchId()函数会返回成功,如果设备具备Touch ID功能。

如果没有,则该函数将返回错误,如果touchIDNotEnrolled尚未设置,则会给出以下错误代码。

LAError.Code.touchIDNotEnrolled.rawValue

您可以使用此值来处理它。

0

整合 Touch ID

现在我们进入教程的主要部分,即将 Touch ID 与应用程序进行整合。事实证明,苹果公司为访问 Touch ID 编写了一些相当标准的代码。此代码来自本地认证框架,如下所示:

LAContext *myContext = [[LAContext alloc] init];

NSError *authError = nil;

NSString *myLocalizedReasonString = @"Used for quick and secure access   to the test app";

 if ([myContext canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics    error:&authError]) {[myContext    evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
              localizedReason:myLocalizedReasonString
                        reply:^(BOOL success, NSError *error) {
          if (success) {
          // User authenticated successfully, take appropriate action
          } 
         else {
            // User did not authenticate successfully, look at error and take appropriate action
         }
    }];
 } 
 else {
// Could not evaluate policy; look at authError and present an appropriate message to user }

让我们逐行查看代码:

第1行:在这里,我们创建了一个LAContext对象。LAContext类负责处理身份验证的上下文。简单来说,我们使用LAContext对象来检查某种类型的身份验证是否可用。在本教程中,我们将稍后检查“如果”触摸ID是一种选项。

第2行:我们需要一个NSError,以便LAContext在出现错误时返回它。

第3行:我们设置了一个NSString,其中包含要放在屏幕上的描述,以让用户知道为什么触摸ID视图已出现在屏幕上。

第5行:这是我们通过调用canEvaluatePolicy:方法并将LAPolicy常量作为参数发送给它来使用LAContext常量的地方。在这种情况下,我们传递LAPolicyDeviceOwnerAuthenticationWithBiometrics。如果失败,则可能是因为触摸ID没有配置在兼容设备上,或者触摸ID不可用于设备...比如iPhone 4S、5或5c运行该应用程序。此外,这也没有考虑到运行iOS 7的设备,因此,如果您计划在应用程序上运行指纹身份验证,请确保检查您是否正在使用兼容的设备,如果不是,请提供其他选项,例如密码或PIN码以访问应用程序。

第6、7和8行:如果用户可以使用生物识别进行身份验证,那么我们现在可以在我们的LAContext对象上调用evaluatePolicy方法。我们通过传递相同的常量(LAPolicyDeviceOwnerAuthenticationWithBiometrics),以及传递我们的原因字符串,并指定一个块来处理响应来完成这个操作。

我们将得到一个YES或NO的结果。如果是YES,那么第10行是我们放置正面响应代码的地方。同样,第12行是我们放置失败代码的地方。

最后在第15行,我们有ELSE语句,如果第5行测试失败,即生物识别不可用,则运行该语句。我们可以检查authError指针以获取原因,并在需要时向用户呈现。

最后,为了使其不显示错误,我们需要将本地身份验证框架导入到我们的项目中:

因此,让我们将此代码添加到我们的项目中。打开ViewController.m,在顶部导入本地身份验证框架。

更多详细信息请访问此链接:http://www.devfright.com/touch-id-tutorial-objective-c/


1
你的回答应该简洁明了,不要像完整的教程一样。 - Ramaraj T
Ramraj T,如果你需要更多关于设备具有Touch ID的信息,请访问此教程链接(http://code.tutsplus.com/tutorials/ios-8-integrating-touch-id--cms-21949),它一定会对你有所帮助。 - Brajbhooshan Singh Tomar

0

您可以通过检查以下所示的错误代码来确定设备是否支持生物识别扫描(touchID和faceID):

func deviceSupportsBiometricScanning() -> Bool {
    var authError: NSError?
    let _ = LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError)
    return authError?.code != kLAErrorBiometryNotAvailable.hashValue
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接