touchIDLockout在iOS 11.0中已弃用

使用Xcode 9为IOS11编译我的应用程序时,我收到以下警告:

warning: 'touchIDLockout' was deprecated in iOS 11.0: use LAErrorBiometryLockout warning: 'touchIDNotEnrolled' was deprecated in iOS 11.0: use LAErrorBiometryNotEnrolled warning: 'touchIDNotAvailable' was deprecated in iOS 11.0: use LAErrorBiometryNotAvailable 

我正在使用touchID但我没有使用touchIdLockout … cste并且touchID正常工作。

我该如何删除这些警告?


编辑(不是由原作者):

我将此追踪到了一个原因。 从我的代码中的LocalAuthentication框架引用LAError足以显示这些警告。

重现的步骤(在Xcode 9.2中尝试过):

  1. 创建一个新的iOS应用程序(单一视图模板)。 请注意,iOS部署目标设置为iOS 11.2。
  2. 将这些行添加到AppDelegate.swift

     import LocalAuthentication 

    appDidFinishLaunching中的appDidFinishLaunching

     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { let _: LAError? = nil return true } 
  3. 构建应用程序。

let _: LAError? = nil let _: LAError? = nil line足以使三个警告出现。 但是,警告与任何特定代码行都没有关联。 它们出现在构建日志中,没有任何文件/行引用:

 :0: warning: 'touchIDLockout' was deprecated in iOS 11.0: use LAErrorBiometryLockout :0: warning: 'touchIDNotEnrolled' was deprecated in iOS 11.0: use LAErrorBiometryNotEnrolled :0: warning: 'touchIDNotAvailable' was deprecated in iOS 11.0: use LAErrorBiometryNotAvailable 

这是一个截图: Xcode中警告 的屏幕 截图

一个示例项目: 下载示例项目(Xcode 9.2)

作为参考,我向Apple报告了此情况。 雷达#36028653。

简短的回答:它看起来像一个编译器错误,由导入C枚举引起,它定义了具有相同值的多个常量。

答案长:不幸的是我没有解决方法如何避免弃用警告,只有可能的解释是什么原因造成的。

LAError代码在LocalAuthentication框架中的中定义为C枚举。 以下是该定义的摘录:

 // Error codes #define kLAErrorAuthenticationFailed -1 #define kLAErrorUserCancel -2 // ... #define kLAErrorTouchIDNotAvailable -6 #define kLAErrorTouchIDNotEnrolled -7 #define kLAErrorTouchIDLockout -8 // ... #define kLAErrorBiometryNotAvailable kLAErrorTouchIDNotAvailable #define kLAErrorBiometryNotEnrolled kLAErrorTouchIDNotEnrolled #define kLAErrorBiometryLockout kLAErrorTouchIDLockout typedef NS_ENUM(NSInteger, LAError) { LAErrorAuthenticationFailed = kLAErrorAuthenticationFailed, LAErrorUserCancel = kLAErrorUserCancel, // ... LAErrorTouchIDNotAvailable NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use LAErrorBiometryNotAvailable") = kLAErrorTouchIDNotAvailable, LAErrorTouchIDNotEnrolled NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use LAErrorBiometryNotEnrolled") = kLAErrorTouchIDNotEnrolled, LAErrorTouchIDLockout NS_ENUM_DEPRECATED(10_11, 10_13, 9_0, 11_0, "use LAErrorBiometryLockout") __WATCHOS_DEPRECATED(3.0, 4.0, "use LAErrorBiometryLockout") __TVOS_DEPRECATED(10.0, 11.0, "use LAErrorBiometryLockout") = kLAErrorTouchIDLockout, // ... LAErrorBiometryNotAvailable NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryNotAvailable, LAErrorBiometryNotEnrolled NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryNotEnrolled, LAErrorBiometryLockout NS_ENUM_AVAILABLE(10_13, 11_0) __WATCHOS_AVAILABLE(4.0) __TVOS_AVAILABLE(11.0) = kLAErrorBiometryLockout, // ... } NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0); 

可以看到“旧”(已弃用)和“新”错误代码使用相同的值。 例如, LAErrorTouchIDNotAvailableLAErrorBiometryNotAvailable都定义为-6

这在C中完全有效,但Swift enum的原始值必须是相互不同的。 显然,Swift导入器通过将新/重复的情况映射到静态变量来解决这个问题。

这是Swift映射的摘录:

 public struct LAError { public init(_nsError: NSError) public static var _nsErrorDomain: String { get } public enum Code : Int { case authenticationFailed case userCancel // ... @available(iOS, introduced: 8.0, deprecated: 11.0, message: "use LAErrorBiometryNotAvailable") case touchIDNotAvailable @available(iOS, introduced: 8.0, deprecated: 11.0, message: "use LAErrorBiometryNotEnrolled") case touchIDNotEnrolled @available(iOS, introduced: 9.0, deprecated: 11.0, message: "use LAErrorBiometryLockout") case touchIDLockout // ... @available(iOS 11.0, *) public static var biometryNotAvailable: LAError.Code { get } @available(iOS 11.0, *) public static var biometryNotEnrolled: LAError.Code { get } @available(iOS 11.0, *) public static var biometryLockout: LAError.Code { get } // ... } // ... } 

这似乎是弃用警告的原因,也是swift-users邮件列表中报告的问题

  • [swift-users]处理库中不推荐使用的枚举案例

LAError编写一个详尽且无警告的switch语句是LAError


为了certificate我的猜想,我用自定义枚举重现了这个问题:将以下定义添加到macOS 10.13或iOS 11项目的桥接头文件中:

 #import  typedef NS_ENUM(NSInteger, MyEnum) { MyEnumA = 1, MyEnumB = 2, MyEnumC NS_ENUM_DEPRECATED(10_10, 10_13, 8_0, 11_0, "use MyEnumNewC") = 3, MyEnumNewC NS_ENUM_AVAILABLE(10_13, 11_0) = 3, }; 

这被导入到Swift中

  public enum MyEnum : Int { case A case B @available(OSX, introduced: 10_10, deprecated: 10_13, message: "use MyEnumNewC") case C @available(OSX 10_13, *) public static var newC: MyEnum { get } } 

第一个(不同的)枚举值有3个案例,重复值有静态属性。

事实上,任何使用MyEnum触发弃用警告:

 // main.swift: print(MyEnum.A) // Or: let _: MyEnum? = nil // Build log: // :0: warning: 'C' was deprecated in OS X 10.13: use MyEnumNewC 

此外,无法在switch语句中使用新的枚举值:

 func foo(err: MyEnum) { switch err { case .A: print("A") case .B: print("B") case .newC: print("C") } } // Build log: // main.swift:12:9: error: switch must be exhaustive // :0: warning: 'C' was deprecated in OS X 10.13: use MyEnumNewC 

即使编译器(显然)知道这些情况是详尽的:

 func foo(err: MyEnum) { switch err { // Switch must be exhaustive case .A: print("A") case .B: print("B") case .newC: print("C") default: print("default") } } // Build log: // :0: warning: 'C' was deprecated in OS X 10.13: use MyEnumNewC // main.swift:19:9: warning: default will never be executed 

这对我来说看起来像编译器错误。

是的,这些是随着Apple转向iOS 11和FaceID而出现的新警告。 最有可能的是,您正在检查生物识别硬件是否未锁定,是否已注册指纹,以及设备是否具有支持硬件。

这里有一个示例设置:

 import LocalAuthentication ... var authContext = LAContext() var biometricsError: NSError? authContext?.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &biometricsError) 

在iOS 10之前,可以运行如下检查:

 if biometricsError?.code == LAError.touchIDNotAvailable.rawValue { // No hardware } if biometricsError?.code == LAError.touchIDNotEnrolled.rawValue { // No fingerprints } if biometricsError?.code == LAError.touchIDLockout.rawValue { // Locked out } 

注意: iOS 11引入了上述代码的略微变体。 他们不是对每个错误属性使用LAError.touchID ,而是引入了LAError.biometry 。 因此,您将拥有: biometryNotAvailablebiometryNotEnrolledbiometryLockout

Apple似乎更喜欢这种方法,而是:

 if biometricsError?.code == Int(kLAErrorBiometryNotAvailable) { // No hardware } if biometricsError?.code == Int(kLAErrorBiometryNotEnrolled) { // No fingerprints } if biometricsError?.code == Int(kLAErrorBiometryLockout) { // Locked out } 

这种方法摆脱了Xcode的警告。