iOS 11.0中不推荐使用touchIDLockout

时间:2017-09-27 19:03:17

标签: ios swift xcode compilation touch-id

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

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

  4. 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。

4 个答案:

答案 0 :(得分:23)

简短回答:这对我来说看起来像编译错误了 通过导入定义多个常量的C枚举 具有相同的价值。

答案很长:很遗憾,我没有解决方法如何避免弃用 警告,只有可能的解释是什么导致它。

LAError代码定义为C枚举 LocalAuthentication框架中的<LAError.h>。这是一个 该定义的摘录:

// 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);

人们可以看到&#34; old&#34; (已弃用)和&#34; new&#34;错误代码使用 相同的价值观。例如,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的语句。

为了证明我的猜想,我用自定义重现了这个问题 枚举:将以下定义添加到桥接头 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中使用新的枚举值 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
// <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

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

答案 1 :(得分:7)

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

答案 2 :(得分:5)

如上所述,这是编译器中的错误。 Swift团队很清楚,您可能想去vote for the bug。同时,在其上添加手表,以便在修复后可以删除以下变通办法。

为了不得到警告,您需要做的是:不要提及LAError 。将LAError视为Voldemort。

相反,请使用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.")
        }
    }
}

答案 3 :(得分:0)

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

例如,此简单代码生成警告。删除所有对旧代码的引用,并使用奥利弗的方法。

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