ios - swift keychain tutorial
touchIDLockout en desuso en iOS 11.0 (4)
Como se ha señalado, este es un error en el compilador. El equipo Swift está al tanto y es posible que desee ir y bugs.swift.org/browse/SR-6637 . Al mismo tiempo, agregue un reloj para que pueda eliminar la siguiente solución una vez que se haya solucionado.
Lo que debe hacer para no recibir las advertencias es: no mencione LAError
. Piense en LAError
como Voldemort.
En su lugar, utilice la comprobación de errores del estilo de Objective-C. Todos los enums de Error
asignan a un NSError
, que se construye a partir de un dominio y un código. Las constantes para compararlas también se exportan a Swift. Pueden ser nombrados sin advertencias. Por lo tanto, su código podría verse un poco (con suerte, muy poco) así.
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.")
}
}
}
Al compilar mi aplicación con Xcode 9 para IOS11, recibo las siguientes advertencias:
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
Estoy usando touchID pero no estoy usando touchIdLockout ... cste y el touchID está funcionando correctamente.
¿Cómo puedo eliminar estas advertencias?
Editar (no por el autor original):
He rastreado esto a una sola causa. Es suficiente para hacer referencia a LAError
desde el marco de Autenticación Local en mi código para que aparezcan estas advertencias.
Pasos para reproducir (probado en Xcode 9.2):
- Crear una nueva aplicación de iOS (plantilla de vista única). Tenga en cuenta que el objetivo de implementación de iOS está configurado en iOS 11.2.
Agregue estas líneas a
AppDelegate.swift
:import LocalAuthentication
Y una sola línea en
appDidFinishLaunching
:func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { let _: LAError? = nil return true }
Construye la aplicación.
El let _: LAError? = nil
let _: LAError? = nil
línea let _: LAError? = nil
es suficiente para hacer aparecer las tres advertencias. Sin embargo, las advertencias no están asociadas con ninguna línea de código en particular. Aparecen en el registro de compilación sin ninguna referencia de archivo / línea:
<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
Aquí hay una captura de pantalla: Captura de pantalla de las advertencias en Xcode
Y un proyecto de muestra: Proyecto de muestra para descargar (Xcode 9.2)
Para referencia, informé esto a Apple. Radar # 36028653.
Sí, estas son nuevas advertencias que están presentes cuando Apple se traslada a iOS 11 y FaceID. Lo más probable es que esté verificando si el hardware biométrico no está bloqueado, tiene huellas dactilares registradas y si el dispositivo tiene el hardware compatible.
Aquí un ejemplo de configuración:
import LocalAuthentication
...
var authContext = LAContext()
var biometricsError: NSError?
authContext?.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &biometricsError)
Hasta iOS 10, uno ejecutaría cheques como este:
if biometricsError?.code == LAError.touchIDNotAvailable.rawValue {
// No hardware
}
if biometricsError?.code == LAError.touchIDNotEnrolled.rawValue {
// No fingerprints
}
if biometricsError?.code == LAError.touchIDLockout.rawValue {
// Locked out
}
Nota: iOS 11 introdujo una ligera variante del código anterior. En lugar de utilizar LAError.touchID
para cada propiedad de error, introdujeron LAError.biometry
. Por lo tanto, tendría: biometryNotAvailable
, biometryNotEnrolled
y biometryLockout
.
Apple parece preferir este enfoque, en cambio:
if biometricsError?.code == Int(kLAErrorBiometryNotAvailable) {
// No hardware
}
if biometricsError?.code == Int(kLAErrorBiometryNotEnrolled) {
// No fingerprints
}
if biometricsError?.code == Int(kLAErrorBiometryLockout) {
// Locked out
}
Este método elimina las advertencias de Xcode.
Yo también luché con esto durante mucho tiempo. La respuesta de Oliver me llevó a una solución que funcionó para mí, pero en caso de que haya otros que aún tengan este problema, parece que CUALQUIER referencia a los antiguos valores de LAError producirá las advertencias enumeradas anteriormente.
Por ejemplo, este código simple produce las advertencias. Elimine TODA la referencia a los códigos antiguos y use el enfoque de 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
De hecho, puede ser incluso más simple. Prueba esto:
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
Respuesta corta: Me parece un error de compilación, causado por la importación de una enumeración de C que define varias constantes con el mismo valor.
Respuesta larga: desafortunadamente no tengo una solución para evitar la advertencia de desaprobación, solo una posible explicación de la causa.
Los códigos LAError
se definen como una enumeración de C en <LAError.h>
en el marco de LocalAuthentication. Aquí hay un extracto de esa definición:
// 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);
Se puede ver que los códigos de error "antiguos" (obsoletos) y "nuevos" utilizan los mismos valores. Por ejemplo, tanto LAErrorTouchIDNotAvailable
como LAErrorBiometryNotAvailable
se definen como -6
.
Eso es perfectamente válido en C, pero los valores sin procesar de una enum
Swift deben ser mutuamente distintos. Al parecer, el importador Swift lo resuelve al asignar los casos nuevos / duplicados a variables estáticas.
Aquí hay un extracto del mapeo 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 }
// ...
}
// ...
}
Y esto parece ser la causa de las advertencias de desaprobación, y también del problema informado en la lista de correo de los usuarios de Swift.
que es imposible escribir una declaración de conmutación exhaustiva y sin advertencias para LAError
.
Para demostrar mi conjetura, he reproducido el problema con una enumeración personalizada: agregue la siguiente definición al archivo de encabezado de puente de un proyecto macOS 10.13 o 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,
};
Esto se importa a Swift como
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 }
}
con 3 casos para los primeros valores de enumeración (distintos) y una propiedad estática para el valor duplicado.
Y, de hecho, cualquier uso de MyEnum
desencadena una advertencia de desaprobación:
// main.swift:
print(MyEnum.A) // Or: let _: MyEnum? = nil
// Build log:
// <unknown>:0: warning: ''C'' was deprecated in OS X 10.13: use MyEnumNewC
Además, no es posible usar el nuevo valor de enumeración en una declaración de cambio:
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
incluso si el compilador (aparentemente) sabe que estos casos son exhaustivos:
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
Esto me parece un error de compilación.