【问题标题】:touchIDLockout deprecated in iOS 11.0iOS 11.0 中不推荐使用 touchIDLockout
【发布时间】:2018-03-09 09:17:50
【问题描述】:

使用 Xcode 9 for 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 中的一行:

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

let _: LAError? = nil 行足以使三个警告出现。但是,这些警告与任何特定的代码行无关。它们出现在构建日志中,没有任何文件/行参考:

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

截图如下:Screenshot of the warnings in Xcode

还有一个示例项目: Sample project for download (Xcode 9.2)

作为参考,我向 Apple 报告了这一点。雷达#36028653。

【问题讨论】:

  • @Ole:即使您不使用 touchID...标识符,您能否重现 “弃用警告” 问题?
  • @MartinR 我在问题中添加了更多信息(很抱歉塞巴斯蒂安劫持了这个)。
  • Xcode 10 GM 仍然存在。

标签: ios swift xcode compilation touch-id


【解决方案1】:

是的,这些是 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 的警告。

【讨论】:

  • 感谢您的回复,但不幸的是它没有删除警告,因为我没有使用 LAError.touchIDNotAvailable、LAError.touchIDNotEnrolled 或 LAError.touchIDLockout。我同意这些常量在 IOS11 中已过时,但我没有使用它们!
  • 应该注意,这不是 Apple 似乎更喜欢的方法,而是已确认的编译器错误 - 请参阅上面的答案,使用 kLAError 常量的解决方法 does虽然工作。但有一个警告。我会添加我自己的答案。
【解决方案2】:

简答:对我来说这看起来像是一个编译器错误,导致 通过导入定义多个常量的 C 枚举 具有相同的值。

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

LAError 代码被定义为 C 枚举 &lt;LAError.h&gt; 在 LocalAuthentication 框架中。这是一个 该定义的摘录:

// 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 邮件列表中报告的问题

不可能写出详尽无遗的 LAError 的 switch 语句。


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

#import <Foundation/Foundation.h>

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:
// <unknown>:0: warning: 'C' was deprecated in OS X 10.13: use MyEnumNewC

此外,不能在 a 中使用新的枚举值 切换语句:

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
// <unknown>: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:
// <unknown>:0: warning: 'C' was deprecated in OS X 10.13: use MyEnumNewC
// main.swift:19:9: warning: default will never be executed

这在我看来像是一个编译器错误。

【讨论】:

【解决方案3】:

我也为此苦苦挣扎了很长时间。 Oliver 的回答让我找到了一个对我有用的解决方案,但以防万一还有其他人仍然有这个问题 - 似乎对旧 LAError 值的任何引用都会产生上面列出的警告。

例如,这个简单的代码会产生警告。删除所有对旧代码的引用并使用 Oliver 的方法。

func evaluateAuthenticationPolicyMessageForLA(errorCode: Int) -> String {
    var message = ""

    switch errorCode {
    case LAError.authenticationFailed.rawValue:
        message = "The user failed to provide valid credentials"
    default:
        message = "Can't use any version of old LAError"
    }

    return message
}//evaluateAuthenticationPolicyMessageForLA

事实上,它可以更简单。试试这个:

func evaluateAuthenticationPolicyMessageForBiometricsError(biometricsError: Int) -> String {

    var message = "I would normally leave this blank"

    if biometricsError == kLAErrorBiometryNotAvailable {
        message = "Biometrics are not available on this device"
    }//if not avail

    if biometricsError == kLAErrorBiometryNotEnrolled {
        message = "Biometrics are not enrolled on this device"
    }//if not enrolled

    if biometricsError == kLAErrorBiometryLockout {
        message = "Biometrics are locked out on this device"
    }//if locked out

    return message

}//evaluateAuthenticationPolicyMessageForBiometricsError

【讨论】:

    【解决方案4】:

    如前所述,这是编译器中的一个错误。 Swift 团队知道,您可能想去vote for the bug。同时,在其上添加一个手表,以便您可以在修复后删除以下解决方法。

    为了避免收到警告,您需要做的是:不要提及LAError。将LAError 视为伏地魔。

    改为使用 Objective-C 风格的错误检查。所有Error 枚举都映射到NSError,它们由域和代码构成。用于比较这些的常量也被导出到 Swift。它们可以在没有警告的情况下命名。所以你的代码可能看起来有点(希望非常少)像这样。

    let context = LAContext()
    let text = "Authenticate, please!"
    context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: text) { (success, error) in
        if success {
            print("?")
        } else {
            guard let error = error else {
                return print("Should not happen according to the docs!")
            }
            let nsError = error as NSError
            switch nsError.domain {
            case kLAErrorDomain:
                switch nsError.code {
                case Int(kLAErrorUserCancel):
                    print("User cancelled.")
                case Int(kLAErrorBiometryLockout):
                    print("Biometry lockout.")
                default:
                    print("Unhandled error.")
                }
            default:
                print("Unhandled error domain. Probably will not happen.")
            }
        }
    }
    

    【讨论】:

    • (是的,其他现有答案显示了相同的技术,但没有一个真正将它们结合在一起,也没有提到检查域,所以我想我会写下来。)