web-dev-qa-db-fra.com

touchIDLockout obsolète dans iOS 11.0

Lors de la compilation de mon application avec Xcode 9 pour IOS11, je reçois les avertissements suivants:

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

J'utilise touchID mais je n'utilise pas touchIdLockout ... cste et le touchID fonctionne correctement.

Comment puis-je supprimer ces avertissements?


Modifier (pas par l'auteur original):

Je l'ai retrouvé pour une seule cause. Il suffit de faire référence à LAError du framework LocalAuthentication dans mon code pour que ces avertissements apparaissent.

Les étapes pour reproduire (essayé dans Xcode 9.2):

  1. Créez une nouvelle application iOS (modèle de vue unique). Notez que la cible de déploiement iOS est définie sur iOS 11.2.
  2. Ajoutez ces lignes à AppDelegate.Swift:

    import LocalAuthentication
    

    Et une seule ligne dans appDidFinishLaunching:

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        let _: LAError? = nil
        return true
    }
    
  3. Construisez l'application.

La ligne let _: LAError? = nil suffit à faire apparaître les trois avertissements. Les avertissements ne sont associés à aucune ligne de code particulière. Ils apparaissent dans le journal de construction sans aucune référence de fichier/ligne:

<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

Voici une capture d'écran:Capture d'écran des avertissements dans Xcode

Et un exemple de projet:Exemple de projet à télécharger (Xcode 9.2)

Pour référence, je l'ai signalé à Apple. Radar # 36028653.

19
sebastien

Réponse courte: Pour moi, cela ressemble à un bogue du compilateur, provoqué par l'importation d'une énumération C définissant plusieurs constantes .__ avec la même valeur.

Réponse longue: Malheureusement, je n'ai pas de solution pour éviter la dépréciation Avertissement, seule une explication possible de sa cause. 

Les codes LAError sont définis comme une énumération C dans <LAError.h> dans la structure LocalAuthentication. Voici un extrait De cette définition:

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

On peut voir que "vieux" (obsolète) et les "nouveaux" codes d'erreur utilisent Les mêmes valeurs. Par exemple, LAErrorTouchIDNotAvailable et LAErrorBiometryNotAvailable sont définis en tant que -6.

Cela est parfaitement valable en C, mais les valeurs brutes d'un Swift enum doivent absolument être séparées. Apparemment, l'importateur Swift résout ce problème en En mappant les observations nouvelles/dupliquées à des variables statiques.

Voici un extrait de la cartographie 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 }
        // ...
    }

    // ...
}

Et cela semble être la cause des avertissements de dépréciation, ainsi que du problème signalé sur la liste de diffusion de Swift-users

qu'il est impossible d'écrire une instruction switch exhaustive et sans avertissement pour LAError.


Pour prouver ma supposition, j’ai reproduit le problème avec une énumération Personnalisée: Ajoutez la définition suivante au fichier de pontage D’un projet macOS 10.13 ou 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,
};

Ceci est importé dans Swift en tant que

 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 }
 }

avec 3 observations pour les premières valeurs d'énumération (distinctes) et une propriété statique pour la valeur dupliquée.

Et en effet, toute utilisation de MyEnum déclenche un avertissement de dépréciation:

// main.Swift:
print(MyEnum.A) // Or: let _: MyEnum? = nil

// Build log:
// <unknown>:0: warning: 'C' was deprecated in OS X 10.13: use MyEnumNewC

De plus, il n'est pas possible d'utiliser la nouvelle valeur enum dans une instruction 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

même si le compilateur (apparemment) sait que ces cas sont exhaustifs:

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

Cela ressemble à un bogue de compilateur pour moi.

21
Martin R

Oui, il s’agit de nouveaux avertissements lors du passage d’Apple à iOS 11 et à FaceID. Très probablement, vous vérifiez si le matériel biométrique n'est pas verrouillé, si des empreintes digitales ont été enregistrées et si le périphérique prend en charge le matériel de support.

Voici un exemple mis en place:

import LocalAuthentication

...

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

Jusqu'à iOS 10, on exécuterait des vérifications comme celle-ci:

if biometricsError?.code == LAError.touchIDNotAvailable.rawValue {
  // No hardware
}

if biometricsError?.code == LAError.touchIDNotEnrolled.rawValue {
  // No fingerprints
}

if biometricsError?.code == LAError.touchIDLockout.rawValue {
  // Locked out
}

Remarque: iOS 11 a introduit une légère variante du code ci-dessus. Au lieu d'utiliser LAError.touchID pour chaque propriété d'erreur, ils ont introduit LAError.biometry. Par conséquent, vous auriez: biometryNotAvailable, biometryNotEnrolled et biometryLockout.

Apple semble préférer cette approche, à la place:

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

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

if biometricsError?.code == Int(kLAErrorBiometryLockout) {
  // Locked out
}

Cette méthode supprime les avertissements de Xcode.

7
Oliver Spryn

Comme il a été noté, il s'agit d'un bogue dans le compilateur. L'équipe Swift en est consciente et vous voudrez peut-être aller et voter pour le bogue . En même temps, ajoutez une surveillance afin de pouvoir supprimer la solution de contournement suivante une fois celle-ci corrigée.

Ce que vous devez faire pour ne pas recevoir les avertissements est: ne mentionnez pas LAError. Pensez à LAError comme à Voldemort. 

Au lieu de cela, utilisez la vérification d'erreur de style Objective-C. Toutes les énumérations Error correspondent à une NSError, qui sont créées à partir d'un domaine et d'un code. Les constantes avec lesquelles comparer celles-ci sont également exportées vers Swift. Ils peuvent être nommés sans avertissements. Donc, votre code pourrait ressembler un peu (espérons-le, très peu) à ceci.

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.")
        }
    }
}
4
skagedal

Moi aussi j'ai lutté avec cela pendant très longtemps. La réponse d'Oliver m'a amené à une solution qui fonctionnait pour moi, mais juste au cas où d'autres personnes auraient encore ce problème, il semble que TOUTE référence aux anciennes valeurs de LAError produira les avertissements répertoriés ci-dessus.

Par exemple, ce code simple produit les avertissements. Supprimez TOUT référence aux anciens codes et utilisez l'approche d'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

En fait, cela peut être encore plus simple. Essaye ça:

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
0
user2698617