Xcode 9.3(beta)中LABiometryType兼容问题及iOS11.0 FaceID适配咨询
这个问题我之前帮同行排查过,本质是Xcode版本更新带来的API版本标注修正——在Xcode 9.2里,LABiometryType被标记为iOS 11.0+可用,但苹果在Xcode 9.3 Beta里纠正了这个信息,明确LABiometryType(尤其是faceID枚举值)实际只在iOS 11.0.1及以上才正式提供稳定支持,这就导致你原来的版本判断逻辑触发了编译错误。
针对你问的「怎么兼顾iOS 11.0和11.0.1+的Face ID处理」,这里有两种靠谱的解决方案:
方案一:运行时动态区分系统版本
因为iOS 11.0的系统里其实是存在LABiometryType相关逻辑的(比如首发Face ID的iPhone X初始系统就是iOS 11.0),只是Xcode 9.3的编译器不认这个版本兼容,所以我们可以用运行时系统版本判断结合API检查来绕开编译限制:
if #available(iOS 11.0, *) { // 先判断是否是iOS 11.0.1及以上版本 let isiOS11_0_1OrLater = ProcessInfo.processInfo.operatingSystemVersion >= OperatingSystemVersion(majorVersion: 11, minorVersion: 0, patchVersion: 1) if isiOS11_0_1OrLater { // 11.0.1+版本,直接正常使用LABiometryType枚举判断 switch laContext.biometryType { case .faceID: print("FaceId support") case .touchID: print("TouchId support") default: print("No Biometric support") } } else { // 纯iOS 11.0版本:无法直接用LABiometryType,换两种方式判断 let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { // 方式1:通过设备型号判断(iOS 11.0只有iPhone X支持Face ID) let deviceModel = UIDevice.current.modelIdentifier if deviceModel == "iPhone10,3" || deviceModel == "iPhone10,6" { print("FaceId support") } else { print("TouchId support") } } else { print("No Biometric support") } } } else { // iOS 11以下的降级处理逻辑 print("Fallback on earlier versions") }
上面用到的modelIdentifier需要给UIDevice加个扩展来实现:
extension UIDevice { var modelIdentifier: String { var systemInfo = utsname() uname(&systemInfo) let machineMirror = Mirror(reflecting: systemInfo.machine) let identifier = machineMirror.children.reduce("") { identifier, element in guard let value = element.value as? Int8, value != 0 else { return identifier } return identifier + String(UnicodeScalar(UInt8(value))) } return identifier } }
方案二:条件编译适配不同Xcode版本
如果你的项目需要同时兼容Xcode 9.2和9.3 Beta编译(比如团队里有人用旧版本Xcode),可以用Swift版本的条件编译来区分:
if #available(iOS 11.0, *) { // Xcode 9.3及以上用的是Swift 4.1,这里会触发新的版本校验 #if swift(>=4.1) if #available(iOS 11.0.1, *) { switch laContext.biometryType { case .faceID: print("FaceId support") case .touchID: print("TouchId support") default: print("No Biometric support") } } else { // iOS 11.0的处理逻辑和方案一的else分支一致 let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { let deviceModel = UIDevice.current.modelIdentifier if deviceModel == "iPhone10,3" || deviceModel == "iPhone10,6" { print("FaceId support") } else { print("TouchId support") } } else { print("No Biometric support") } } #else // Xcode 9.2及以下用Swift 4.0,直接用原来的逻辑即可 switch laContext.biometryType { case .faceID: print("FaceId support") case .touchID: print("TouchId support") default: print("No Biometric support") } #endif } else { // iOS 11以下的降级处理 print("Fallback on earlier versions") }
额外建议
其实苹果调整LABiometryType的版本要求,是因为iOS 11.0里的Face ID存在一些稳定性bug,正式的完善支持是从iOS 11.0.1开始的。如果你的App可以接受在iOS 11.0版本不精确区分Face ID和Touch ID,也可以直接把版本判断改成#available(iOS 11.0.1, *),然后在iOS 11.0的情况下只提示「生物识别支持」,这样代码会简洁很多,也能避免设备型号判断的维护成本。
内容的提问来源于stack exchange,提问作者Krunal

