【问题标题】:How to programmatically check support of 'Face Id' and 'Touch Id'如何以编程方式检查对“Face Id”和“Touch Id”的支持
【发布时间】:2018-04-03 21:28:18
【问题描述】:

出于我的应用安全目的,我已集成 LocalAuthentication,它一直支持基于 'Touch Id' 的支持。但是现在,Apple 最近也添加了基于 'Face Id' 的身份验证。

我如何检查设备支持哪种类型的身份验证。 Touch Id 还是 Face Id?

【问题讨论】:

标签: ios ios11 touch-id face-id localauthentication


【解决方案1】:

使用 Xcode 9,查看LocalAuthentication -> LAContext -> LABiometryType

LABiometryType 是一个枚举,其值如附图中所示

您可以在 Touch ID 和 FaceID 之间检查设备支持的身份验证类型或不支持。

编辑:

Apple 已更新此枚举 LABiometryType 的值。 现在没有弃用

使用 Swift 5 检查支持的生物识别类型的扩展:

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 {
            return .none
        }

        if #available(iOS 11.0, *) {
            switch self.biometryType {
            case .none:
                return .none
            case .touchID:
                return .touchID
            case .faceID:
                return .faceID
            @unknown default:
                #warning("Handle new Biometric type") 
            }
        }
        
        return  self.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) ? .touchID : .none
    }
}

【讨论】:

  • 在刚刚发布的 Xcode 9.2 测试版中,在 iOS 11.2 中,LABiometryType 枚举值已更改为 faceIDtouchID
  • @tfrank377 感谢您提醒我。我已经更新了答案。
  • 我们能否仅使用模拟器检测设备是否支持 Face ID 或 touch ID?
  • 是的,您可以使用模拟器进行测试。选择 Simulator -> Hardware -> Touch ID -> Cases 这将为基于模拟器的两者提供支持。
  • 您还没有回答问题。问题不在于它是什么?但是如何/>
【解决方案2】:

我一直在努力让它工作,发现我需要使用 LAContext 的单个实例并且需要调用 LAContextInstance.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) 之前获取 biometryType。这是我支持旧 iOS 版本的最终代码:

import LocalAuthentication

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
}

【讨论】:

  • 谢谢。正是我想要的:)
  • biometryType 的文档指出:“仅当 canEvaluatePolicy 对生物识别策略成功时才设置此属性”。因此,如果 canEvaluatePolicy 失败(例如,touchid/faceid 完全或每个应用程序都被停用)biometricType() 返回.none。所以biometricType() 不是检查硬件的可用性,而是检查应用程序是否可以访问硬件。
  • @ValeriyVan 你有没有办法检查设备上硬件的可用性?似乎每个人都在给出 biometricType 作为答案,但是,就像你说的那样,如果你只是想向用户展示一个按钮,上面写着“Face ID”或“Touch ID”以便用户当设备还不能通过生物识别技术进行身份验证时可以授权一个或另一个。
  • @SAHM,实际上我没有找到比检查设备类型更好的方法。这是不好的方式,因为它不是未来的证据。希望苹果能更新 API 来解决这个问题。
  • 这很有趣,因为他们实际上说“不要在支持 Face ID 的设备上引用 Touch ID。相反,不要在支持 Touch ID 的设备上引用 Face ID。检查设备的功能和使用适当的术语。有关开发人员指南,请参阅 LABiometryType。但是除非用户已经授权,否则没有办法做到这一点。 developer.apple.com/ios/human-interface-guidelines/…
【解决方案3】:

这是通过属性的另一种方式(例如,在您的访问实例上)。

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

【讨论】:

  • 我们能否仅使用模拟器检测设备是否支持 Face ID 或 touch ID?
【解决方案4】:

目标 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;
    }
}

【讨论】:

    【解决方案5】:

    此代码在 Xcode 9.2-9.4 上构建时没有警告(请参阅 cmets 了解 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
    }
    

    【讨论】:

      【解决方案6】:

      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
      }
      

      【讨论】:

      • 我们能否仅使用模拟器检测设备是否支持 Face ID 或 touch ID?
      • @shaqirsaiyed 是的。我们可以检测设备是否支持 Face ID 或 Touch ID。当您在 iPhoneX 或更高版本的设备上运行该应用程序时,它会自动检测 Face ID,否则对于 iPhone 8 或 iPhone 8Plus 设备,它将检测 Touch ID。
      • 考虑我是否没有任何物理设备并在模拟器中运行。那我能检测到吗?
      【解决方案7】:

      因为我是扩展的忠实粉丝。我对这个答案的措辞略有不同。本质是一样的。这是一个插入式扩展。

      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
      

      【讨论】:

        【解决方案8】:

        这是我的“帮助类”,它还包括密码

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

        【讨论】:

          【解决方案9】:

          来自@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
              }
          }
          

          }

          【讨论】:

            【解决方案10】:

            我为本地身份验证创建了一个单例类,因为它有助于在整个应用程序中使用 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")
                    }
             }
            

            【讨论】:

              【解决方案11】:
              -(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;
              
              }
              

              【讨论】:

                【解决方案12】:

                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 > 已注册。

                【讨论】:

                  【解决方案13】:

                  请参考苹果为“使用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

                  【讨论】:

                    【解决方案14】:

                    开始在 12 Pro 上测试一些新应用,发现我发布的应用只有 Touch ID,没有 Face ID。

                    我来到这里并看到了这一切,所以我开始尝试更改我的 Touch ID 代码,但我需要做的就是将隐私密钥添加到 info.plist。

                    信息属性列表➕

                    然后向下滚动到:Privacy-Face ID Usage Description, (Type: String), (Value: YES)

                    太简单了?

                    【讨论】:

                      【解决方案15】:

                      您需要在案例中添加@unknown!!!

                      在较新的版本(Xcode 13...)中,您可能必须指定“未知”

                      #warning("Handle new Biometric type") 不返回值

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

                      【讨论】:

                        猜你喜欢
                        • 2020-02-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2023-01-26
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-08-29
                        • 1970-01-01
                        相关资源
                        最近更新 更多