【iOS 11】LocalAuthenticationでFace IDとTouch IDの認証を実装する


## はじめに

(~~開発者泣かせの~~)iPhone X が発売されましたねー
生体認証として iPhone 5s から慣れ親しんだ Touch ID による
**指紋認証**に加え，Face ID による **顔認証**も増えました。
ただ使えるのは iPhone X だけですね。
今後他の iOS 端末や MacBook にも広がる可能性は大いにありそうです。

今回は，Face ID や Touch ID を用いた認証の実装をやってみたので書きます。
結論から言うと，今まで Touch ID による認証を用いていた場合は，
enum の追加分の条件分岐が増えるくらいであまりいらない感じです。

## サンプルコード

サンプルコードを GitHub にアップしましたので，
気になる方がいらっしゃいましたらみてみてください。

### GitHub

https://github.com/MilanistaDev/AuthenticationTest 

### 開発環境

macOS Sierra 10.12.6
Xcode 9.1, 9.2 beta
Swift 4.0
Target iOS 11.0 以降

スクショ等は Xcode 9.1 で実行したものを使用しています。
もっとこう書いた方が良い，ここは間違っているなど気軽にご教示お願いいたします。


## 実装

### 1. Info.plist に `NSFaceIDUsageDescription` を追加

`NSFaceIDUsageDescription` で許可を求めるようにする。

![NSFaceIDUsageDescription.png](https://qiita-image-store.s3.amazonaws.com/0/88266/b6c06765-c52e-d7f6-3ec7-f3cf4d36d576.png "NSFaceIDUsageDescription.png")

顔認証が使える場合は認証が必要な際に
下記のいつものダイアログが表示されます。

![許可.png](https://qiita-image-store.s3.amazonaws.com/0/88266/60807c22-a15c-651a-f6bd-4b95666d311c.png "許可.png")

書かない場合の差異については下記記事が詳しいです。
https://qiita.com/Hackenbacker/items/765db49b2a6f46287713


### 2. LocalAuthentication Framework を import

昔やってたような Xcode の framework の追加の作業は不要でした。 

```Swift:ViewController.swift
import LocalAuthentication // 追加
```

### 3. 認証のコードを実装

認証だけなら最低書くコードは下記です。
Face ID と Touch ID が共存しているデバイスはないので
どちらか，あるいはどちらもできないかになりそれぞれ場合分けする感じですかね。

```swift:ViewController.swift
// viewDidLoad など適切な場所で
let context = LAContext()
var error: NSError?
let description: String = "認証"
 
// Touch ID・Face IDが利用できるデバイスか確認する
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
    // 利用できる場合は指紋・顔認証を要求する
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: description, reply: {success, evaluateError in
        if (success) {
            // 認証成功時の処理を書く
            print("認証成功")
        } else {
            // 認証失敗時の処理を書く
            print("認証失敗")
        }
    })
} else {
    // Touch ID・Face IDが利用できない場合の処理
    let errorDescription = error?.userInfo["NSLocalizedDescription"] ?? ""
    print(errorDescription) // Biometry is not available on this device.
}
```

### 4. Touch ID か Face ID かを判別する

iOS 11 から `LocalAuthentication` Framework に
`LABiometryType` が追加されています。
Xcode 9 以降じゃないと警告になるので注意。
下記のように enum で定義されており，`none`・`typeTouchID`・`typeFaceID` の 3 種類。

```objc:LAContext.h
// Xcode 9.1
@available(iOS 11.0, *)
public enum LABiometryType : Int {

    /// The device does not support biometry.
    case none

    /// The device supports Touch ID.
    case typeTouchID

    /// The device supports Face ID.
    case typeFaceID
}
```

**※ Xcode 9.2 beta ではもっと Swift らしい値になっています。**
個人的にはこちらの方がしっくりきます。

```objc:
@available(iOS 11.0, *)
public enum LABiometryType : Int {

    /// The device does not support biometry.
    @available(iOS 11.2, *)
    case none

    /// The device does not support biometry.
    @available(iOS, introduced: 11.0, deprecated: 11.2, renamed: "LABiometryType.none")
    public static var LABiometryNone: LABiometryType { get }

    /// The device supports Touch ID.
    case touchID

    /// The device supports Face ID.
    case faceID
}
```

そこで，認証時に出すメッセージをそれぞれの値で出し入れできるようにしてみました。
Touch ID・Face ID が利用できるデバイスか確認した後に下記コードを実装します。

```swift:ViewController.swift
switch context.biometryType {
case .typeFaceID: // Xcode 9.2 以上は .faceID
    description = "アカウント情報を閲覧するためにFace IDを認証として用います。"
    break
case .typeTouchID: // Xcode 9.2 以上は .touchID
    description = "アカウント情報を閲覧するためにTouch IDを認証として用います。"
    break
case .none:
    description = "アカウント情報を閲覧するためにログインしてください。"
    break
}
```

サンプルコードでは，認証ができない場合は
ログイン認証もどきのアラートを表示するようにしています。
`Face ID` ではシミュレータ以外で動作確認できていませんが
ドキュメント見る限りではこの実装で良さそうです。
(不備があったら教えてください)

## シミュレータによる動作確認

デフォルトでビルドした場合は，Face ID は無効になっています。
シミュレータのメニューで **Hardware -> Face ID** と辿り，

![Hardware -> FaceID.png](https://qiita-image-store.s3.amazonaws.com/0/88266/0286504e-9932-3ac5-ec8e-3f22374cddf4.png "Hardware -> FaceID.png")

**Enrolled** をタップするとチェックが入ります。
これで Face ID が有効になります。
その下の 2 項目はもう分かりますよね。

![Enrolled.png](https://qiita-image-store.s3.amazonaws.com/0/88266/420af305-8fe6-bef3-c592-5a174447503c.png "Enrolled.png")

サンプルコードを実行して `Enrolled` にチェックを入れた場合，
ボタン押下で許可を求めるダイアログが出ます。

![first_dialog.png](https://qiita-image-store.s3.amazonaws.com/0/88266/ebf9dab7-b1e5-1adf-dc20-29a9a00f6970.png "first_dialog.png")


OK ボタンを押すと，実機だと多分 Finder アイコンチックな Face ID のロゴ付きの
ダイアログがおそらく表示されると思うのですが，シミュレータでは寂しいものが出てきます。

![faceid_wait.png](https://qiita-image-store.s3.amazonaws.com/0/88266/11486655-c5a0-d820-bdfd-be57149c2c66.png "faceid_wait.png")

この状態で Enrolled の下の `Matching Face` を選択してみると・・・

![matchingface.png](https://qiita-image-store.s3.amazonaws.com/0/88266/3065ac21-b1c2-91da-c3ca-c3f76a3d4aa5.png "matchingface.png")

当然ながら認証に成功しました。

![success.png](https://qiita-image-store.s3.amazonaws.com/0/88266/1656bf8b-e788-17a7-2d2c-c92c1345a7ea.png "success.png")

逆に `Non-matching Face` を選んでみると・・・

![notmatching.png](https://qiita-image-store.s3.amazonaws.com/0/88266/dda03229-1fbf-86a1-f754-90b4e9f0649e.png "notmatching.png")

認証失敗してしまいますね。

![failed.png](https://qiita-image-store.s3.amazonaws.com/0/88266/4ccdf1f2-7535-bc4b-dc07-930160cb35ed.png "failed.png")

## 追記 2017.11.06
実機を手に入れたので動作確認してみました。Face ID ちゃんがやはり出るようです。

### ボタンを押して 1 回で認証成功
![successFirstTry.gif](https://qiita-image-store.s3.amazonaws.com/0/88266/1460f416-70ba-9984-40fc-eb1d044ff1e0.gif)

### ボタンを押して 1 回失敗し次で認証成功
ダイアログがでてそのダイアログ内で Face ID ちゃんがぐるぐる。緑になる。
![success_at_twice.gif](https://qiita-image-store.s3.amazonaws.com/0/88266/b39396b7-c9c1-257f-1dfa-de70126a661f.gif)

### 失敗時
![failed.gif](https://qiita-image-store.s3.amazonaws.com/0/88266/f0a6eb10-44b8-3281-43cc-c46c4c1c6ac9.gif)


## おわりに

今回は，`LocalAuthentication` での Face ID・Touch ID 認証について書きました。
iPhone X の顔認証って実際認証速度や精度ってどんな感じなんでしょうね。
enum の定義的に今後どちらの認証にも対応した端末出ないってことなのかな？
出た場合はどちらかを認証として用いるという項目作ってユーザに選んでもらうのもありかも。

私の現場ではこういう書き方してるよーとか，
実機で動作確認できていないのでここうまく動かないよ？とか
ここは間違ってるよ？とかあればご指摘いただければ嬉しいです。

ここまでご覧いただきありがとうございました。

この記事は前に書いたブログを新しい情報を加えて Qiita 用に書き換えたものです。

* __【Swift 4.0】LocalAuthenticationでFace IDとTouch IDの認証を実装する__
https://milanista224.blogspot.jp/2017/09/LocalAuthentication.html

## 参考

* LocalAuthentication
https://developer.apple.com/documentation/localauthentication 

* LABiometryType
https://developer.apple.com/documentation/localauthentication/labiometrytype 

* [iPhone X] 「Face IDには完全に対応してしない可能性があります。」対処法
https://qiita.com/Hackenbacker/items/765db49b2a6f46287713