touchIDLockout在iOS 11.0中已弃用

seb*_*ien 19 xcode compilation ios touch-id swift

使用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
Run Code Online (Sandbox Code Playgroud)

我正在使用touchID,但我没有使用touchIdLockout ... cste,touchID工作正常.

我该如何删除这些警告?


编辑(不是原作者):

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

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

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

    import LocalAuthentication
    
    Run Code Online (Sandbox Code Playgroud)

    还有一行appDidFinishLaunching:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let _: LAError? = nil
        return true
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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
Run Code Online (Sandbox Code Playgroud)

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

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

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

Mar*_*n R 23

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

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

LAError代码在定义为一个C枚举 <LAError.h>在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);
Run Code Online (Sandbox Code Playgroud)

可以看到"旧"(已弃用)和"新"错误代码使用相同的值.例如,既 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 }
        // ...
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

无法为其编写详尽且无警告的switch语句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,
};
Run Code Online (Sandbox Code Playgroud)

这被导入到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 }
 }
Run Code Online (Sandbox Code Playgroud)

第一个(不同的)枚举值有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
Run Code Online (Sandbox Code Playgroud)

此外,无法在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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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


Oli*_*ryn 7

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

这里有一个示例设置:

import LocalAuthentication

...

var authContext = LAContext()
var biometricsError: NSError?
authContext?.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &biometricsError)
Run Code Online (Sandbox Code Playgroud)

在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
}
Run Code Online (Sandbox Code Playgroud)

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

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

if biometricsError?.code == Int(kLAErrorBiometryNotAvailable) {
  // No hardware
}

if biometricsError?.code == Int(kLAErrorBiometryNotEnrolled) {
  // No fingerprints
}

if biometricsError?.code == Int(kLAErrorBiometryLockout) {
  // Locked out
}
Run Code Online (Sandbox Code Playgroud)

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

  • 感谢您的回复,但不幸的是它没有删除警告,因为我没有使用LAError.touchIDNotAvailable,LAError.touchIDNotEnrolled或LAError.touchIDLockout.我同意这些常量在IOS11中已经过时但我没有使用它们! (3认同)

ska*_*dal 5

如前所述,这是编译器中的错误。Swift团队意识到这一点,您可能想去对该错误进行投票。同时,在其上添加手表,以便在修复后可以删除以下变通办法。

为了不得到警告,您需要做的是:不要提及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.")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)