【问题标题】:Determine if an iOS device supports TouchID without setting passcode在不设置密码的情况下确定 iOS 设备是否支持 TouchID
【发布时间】:2015-05-30 13:13:37
【问题描述】:

我目前正在开发一个 iOS 应用程序,它允许用户使用 TouchID 登录应用程序,但首先他们必须先在应用程序内设置密码。问题是,要显示启用 TouchID 登录的设置密码选项,我需要检测 iOS 设备是否支持 TouchID。

使用 LAContext 和 canEvaluatePolicy(就像这里的答案 If Device Supports Touch ID),我能够确定当前设备是否支持 TouchID如果用户在他们的 iOS 设备上设置了密码。这是我的代码 sn-p(我使用的是 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;
}

如果用户没有设置设备密码,无论设备是否真正支持 TouchID,authError 都会返回“PasscodeNotSet”错误。

如果用户的设备支持 TouchID,我希望始终在我的应用中显示 TouchID 选项,无论用户是否在其设备上设置了密码(我只会警告用户先在其设备上设置密码)。反之亦然,如果用户的设备不支持 TouchID,我显然不想在我的应用中显示 TouchID 选项。

所以我的问题是,有没有一种很好的方法可以始终如一地确定 iOS 设备是否支持 TouchID,而不管用户是否在他们的设备上设置了密码?

我能想到的唯一解决方法是确定设备的架构(在Determine if iOS device is 32- or 64-bit 中回答),因为 TouchID 仅在具有 64 位架构的设备上受支持。不过,我正在寻找是否有更好的方法来做到这一点。

【问题讨论】:

    标签: ios iphone ipad xamarin touch-id


    【解决方案1】:

    检测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#。

    您应该避免检查系统版本,而只检查类或方法是否可用。

    【讨论】:

    • 感谢您的回答,但恐怕这不是我正在寻找的解决方案。据我所知,LAContext 类在所有运行 iOS8 的设备上都可用。但是在设备运行 iOS8 但没有 TouchID 的情况下,if 子句将为 true,NSError 仍将返回 PasscodeNotSet...如果我在这里弄错了,请纠正我 :)
    • 是的,但是如果无法使用,为什么还要检测设备是否有touchID呢?
    • 好点;我的回答是,这是我团队的设计决定,即使用户尚未设置密码,我们仍将为所有具有 TouchID 的设备显示 TouchID 选项。我们将警告用户他们需要先在他们的设备上设置密码和指纹,但我们想证明可以在他们的设备上使用 TouchID。我们可能需要重新评估这个设计决策——但我还是想知道这样的事情是否可能:)
    • 听起来像是一个正当的理由,但也有没有 TouchID 的 64 位设备,例如 iPad air 和 iPad Mini 视网膜。您应该向 Apple 提交错误报告,以便他们在 SDK 更新中修复此问题。
    • 啊,真的,我忘了 2013 年的 iPad air 和 iPad mini。所以就目前而言,结论是不可能做这样的事情。我会尽快向 Apple 提交错误报告。谢谢@rckoenes!
    【解决方案2】:

    在下面的讨论结束时,当用户尚未在其设备上设置密码时,暂时无法确定设备是否真正支持 TouchID。

    我已向 Apple 错误报告者报告了这个 TouchID 缺陷。想关注这个问题的人可以在 Open Radar 上看到它:http://www.openradar.me/20342024

    感谢@rckoenes 的输入:)

    编辑

    原来有人已经报告了类似的问题 (#18364575)。以下是 Apple 对此问题的回复:

    “工程部门已根据以下信息确定此问题的行为符合预期:

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

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

    所以.....Apple 的最终回答是。 :(

    注意:报告此问题的人提出的类似 StackOverflow 问题 -> iOS8 check if device has Touch ID (想知道为什么我之前没有找到这个问题,尽管我进行了广泛的搜索......)

    【讨论】:

      【解决方案3】:

      这里有一个有点乏味的方法来确定设备是否有物理触摸 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

      【讨论】:

      • 这很有帮助,但请包括“#include ”。
      【解决方案4】:

      我知道这是去年的一个问题,但这个解决方案不能满足您的需求? (斯威夫特代码)

      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
              }
          }
      }
      

      希望对你有帮助!

      【讨论】:

        【解决方案5】:

        对于 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;
            }
          }
        }
        

        【讨论】:

          【解决方案6】:

          对于 iOS 11+,您可以使用 biometryType: LABiometryTypeLAContext。更多来自 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
          }
          

          【讨论】:

            【解决方案7】:

            以下是您可以识别设备是否支持 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
                }
            
            }
            

            以下是上述共享代码的用法

            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")
            }
            

            【讨论】:

            • 流畅流畅,除了“let hasTouchId”可以重命名为“let deviceHasBiometry”
            【解决方案8】:

            对于 iOS 11+,对于上下文错误,您可以检查 kLAErrorBiometryNotAvailable

            【讨论】:

              猜你喜欢
              • 2018-11-30
              • 2014-02-22
              • 2015-05-07
              • 1970-01-01
              • 2017-01-26
              • 2015-06-15
              • 1970-01-01
              • 2012-10-08
              相关资源
              最近更新 更多