アプリケーションをIOS11のXcode 9でコンパイルすると、次の警告が表示されます。
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は正しく機能しています。
これらの警告を削除するにはどうすればよいですか?
これを1つの原因にまで追跡しました。これらの警告を表示するには、コード内のLocalAuthenticationフレームワークからLAError
を参照するだけで十分です。
再現手順(Xcode 9.2で試行):
これらの行をAppDelegate.Swift
に追加します。
import LocalAuthentication
appDidFinishLaunching
の1行:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let _: LAError? = nil
return true
}
アプリをビルドします。
let _: LAError? = nil
行は、3つの警告を表示するのに十分です。ただし、警告は特定のコード行には関連付けられていません。それらはファイル/行参照なしでビルドログに表示されます。
<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
スクリーンショットは次のとおりです:Xcodeの警告のスクリーンショット
そしてサンプルプロジェクト:ダウンロード用サンプルプロジェクト(Xcode 9.2)
参考までに、これをアップルに報告しました。レーダー#36028653。
短い答え:同じ値を持つ複数の定数を定義するC列挙のインポートが原因で、コンパイラのバグのように見えます。
長い答え:残念ながら、非推奨の警告を回避する方法はありません。原因の可能性のある説明のみです。
LAError
コードは、LocalAuthenticationフレームワークの<LAError.h>
のC列挙として定義されています。以下はその定義の抜粋です。
// 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);
「古い」(非推奨)エラーコードと「新しい」エラーコードが同じ値を使用していることがわかります。たとえば、LAErrorTouchIDNotAvailable
とLAErrorBiometryNotAvailable
はどちらも-6
として定義されます。
これはCでは完全に有効ですが、a 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
さらに、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
これはコンパイラのバグのようです。
はい、これらは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
を導入しました。したがって、次のようになります。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
}
このメソッドは、Xcodeの警告を取り除きます。
既に述べたように、これはコンパイラーのバグです。 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.")
}
}
}
私も非常に長い間これに苦労しました。 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