iOS 13 の Core NFC で運転免許証を読み取ろう【TRETJapanNFCReader】

**2019年12月5日 追記**
[Core NFC Advent Calendar 2019](https://qiita.com/advent-calendar/2019/corenfc) にて、こちらの記事の内容を最新にしたものを公開しました。ぜひそちらをご覧ください！

- [iPhone で運転免許証を読み取ってみよう！【ライブラリを使って】](https://qiita.com/treastrain/items/9025000acec37f77134a)


---

WWDC19 にて発表された iOS 13 の Core NFC にて、これまでの NDEF タグの読み書きの他に FeliCa を含む NFC の読み書きができるようになりました。

そこで私は日本の NFC、FeliCa カードの読み取りを行うためのライブラリとして、[treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) を開発しています。

開発途中のため、まだまだできることは少ないですが、少しでも iOS 13 における NFC の利用方法について知ってもらいたいと思い、この記事では運転免許証を読み取るサンプルを紹介させていただきたいと思います。

なお、記事中で紹介するコードは [treastrain/TRETJapanNFCReader 0.0.5](https://github.com/treastrain/TRETJapanNFCReader/releases/tag/0.0.5) の Examples にある DriversLicenseReader にすべて含まれています。

#TRETJapanNFCReader
[treastrain/TRETJapanNFCReader: 日本の NFC、FeliCa カード向けリーダーライブラリ（iOS 13.0 以降） - GitHub](https://github.com/treastrain/TRETJapanNFCReader)

- Suica、PASMOなどの交通系ICカード、運転免許証の読み取り
- 日本語・英語に対応

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">iOS 13.0 以降で使える、日本のNFCカード向けリーダーライブラリを 開発途中ではありますが GitHub に上げました。<a href="https://t.co/ARV6y0aWYe">https://t.co/ARV6y0aWYe</a><br><br>いまは運転免許証をスキャンするところまで公開しております。<br>今日中には運転免許証の公開情報の読み込みまでをアップする予定です。</p>&mdash; treastrain / とれいん (@treastrain) <a href="https://twitter.com/treastrain/status/1144680439128268801?ref_src=twsrc%5Etfw">2019年6月28日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

様々な方から いいね、Star をいただいており、とてもうれしく思っています！

#環境
- Xcode 11 beta 3
- NFC の読み取りが可能な iOS 13.0 端末（iOS 13.0 beta 3 の iPhone X で動作確認しています）
- 運転免許証（いわゆる ICカード免許証、NFC-B (Type-B)）
- [treastrain/TRETJapanNFCReader 0.0.5](https://github.com/treastrain/TRETJapanNFCReader/releases/tag/0.0.5)

残念ながら SwiftUI ではありません……。

#GitHub からダウンロード
まずは GitHub からコード一式を持ってきてください。
[Releases から Tag 0.0.5 をダウンロード](https://github.com/treastrain/TRETJapanNFCReader/releases/tag/0.0.5)することをおすすめします。

GitHub の Examples にあるサンプルは Capabilities や Info.plist の設定が既に済んでいます。README をご確認ください。

#ライブラリのインポート
以降、`DriversLicenseReader`ディレクトリ内を操作します。
すでにサンプルのための ViewController が作成されていますが、ここでは新しく `ViewController.swift` を作成するものとして記事を進めます。
まずはライブラリのインポートから。

```swift:Examples/DriversLicenseReader/ViewController.swift
import UIKit
import CoreNFC
import TRETJapanNFCReader
```

#初期化とデリゲートの継承
運転免許証の読み取りを司る `DriversLicenseReader` を初期化します。
`DriversLicenseReader` は初期化の際に `DriversLicenseReaderSessionDelegate` か `DriversLicenseReaderSessionDelegate` を継承した `UIViewController`、`DriversLicenseReaderViewController` を要求します。

```swift:TRETJapanNFCReader/DriversLicense/DriversLicenseReader.swift
public typealias DriversLicenseReaderViewController = UIViewController & DriversLicenseReaderSessionDelegate
```

`DriversLicenseReader(viewController: )` で初期化すると、エラーが発生した場合にライブラリ内で UIAlertController を作って `viewController.present` します。エラーのときの処理をすべて自前で行う場合は `DriversLicenseReader(delegate: )` で初期化してください。


```swift:Examples/DriversLicenseReader/ViewController.swift
import UIKit
import CoreNFC
import TRETJapanNFCReader

class ViewController: UIViewController, DriversLicenseReaderSessionDelegate {
    
    var reader: DriversLicenseReader!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.reader = DriversLicenseReader(viewController: self)
    }
}
```

#`DriversLicenseReader.get(items: )` を呼ぶ
`DriversLicenseReader` の初期化が済んだら、あとは `DriversLicenseReader.get(items: )` を呼ぶだけです。引数の `items` は `[DriversLicenseCardItem]` を要求します。`DriversLicenseCardItem` は、記事執筆時点で以下のとおりです。

```swift:TRETJapanNFCReader/DriversLicense/DriversLicenseCard.swift
/// 日本の運転免許証から読み取ることができるデータの種別
public enum DriversLicenseCardItem: CaseIterable {
    /// MF/EF01 共通データ要素
    case commonData
    /// MF/EF02 暗証番号(PIN)設定
    case pinSetting
    /// DF1/EF01 記載事項(本籍除く)
    case matters
}
```

ここで気をつけなければいけないのは、ICカード免許証の仕様で `共通データ要素` と `暗証番号(PIN)設定` 以外のデータの読み取りには、ICカード免許証の発行時に各自で設定した暗証番号が必要となることです。
ICカード運転免許証には暗証番号1と暗証番号2の2つがあり、`本籍` を含むデータを読み取る場合は暗証番号1と暗証番号2の両方、それ以外のデータの場合は暗証番号1が必要となっています。
また、**この暗証番号は3回間違えるとロックされ、以降は警察署などでロックを解除してもらわなければ、読み取りの際に暗証番号が必要なデータを読み取ることができなくなってしまいます。**

よって、`items` で `commonData` と `pinSetting` を指定した場合は問題ありませんが、`matters` が含まれている場合（`DriversLicenseCardItem.allCases`とした場合なども同様）は暗証番号1も渡す必要があります。

```swift
self.reader.get(items: [.commonData, .pinSetting])

self.reader.get(items: [.matters], pin1: "暗証番号1")
```

一度 `self.reader.get(items: [.pinSetting])` で ICカード免許証を読み取り、暗証番号が設定されているかどうかを確認することもできます。

#読み取った結果を確認する
`DriversLicenseReader.get(items: )` を呼んだ後は iOS 端末が NFC の読み取りモードに入ります。iPhone の上部（受話部）を ICカード免許証の下半分を隠すように置いて、読み取ります。
読み取りが正しく成功すると、`DriversLicenseReaderSessionDelegate` により `driversLicenseReaderSession(didRead driversLicenseCard: DriversLicenseCard)` が呼ばれます。

```swift:Examples/DriversLicenseReader/ViewController.swift
    func driversLicenseReaderSession(didRead driversLicenseCard: DriversLicenseCard) {
        // driversLicenseCard に読み取った運転免許証の情報が格納されている
    }
}
```

`driversLicenseCard` に読み取った情報が入っているので、確認してみましょう。`DriversLicenseCard` は [TRETJapanNFCReader/DriversLicense/DriversLicenseCard.swift](https://github.com/treastrain/TRETJapanNFCReader/blob/master/TRETJapanNFCReader/DriversLicense/DriversLicenseCard.swift) で定義されています。

#終わりに
以上の手順で簡単に運転免許証のデータを読み取ることができたかと思います。
例えば、いわゆる IDセルフィー と運転免許証の NFC 読み取りを組み合わせれば本人確認の手段として有効に活用できるのではないかなと思います。

もしよろしければ [GitHub](https://github.com/treastrain/TRETJapanNFCReader) のほうに Star していただけるとありがたいです！また、指摘や意見等もお待ちしています。