iPhone で運転免許証を読み取ってみよう！【ライブラリを使って】

それでは Core NFC を用いた iPhone アプリ開発の例として、運転免許証の読み取りを行います。今回は OSS ライブラリを用いて、Core NFC のコーディングよりも Core NFC を使うときに必要な Xcode プロジェクトでの各設定を中心に説明していきます。

# 環境
- 開発
    - Xcode Version 11.2.1 (11B500)
    - Apple Swift version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
    - macOS Catalina 10.15.1（19B88）
- 実機
    - iPhone 11 Pro (A2215、MWCC2J/A)
    - iOS 13.2.3 (17B111)

# Xcode プロジェクトの作成
まずは Create a new Xcode project から iOS の Single View App を作成します。

# ライブラリの導入
今回は私が開発・公開しているライブラリ、[treastrain/TRETJapanNFCReader](https://github.com/treastrain/TRETJapanNFCReader) を使用します。
このライブラリは Swift Package Manager、Carthage、CocoaPods に対応していますが、今回は Carthage を使うことにします。
※運転免許証の読み取りの場合、Swift Package Manager では文字コードの変換を行うことができずに正しく読み取ることができません。

1. `Cartfile` に `github "treastrain/TRETJapanNFCReader"` と記述して `carthage update --platform iOS` する。
2. Project の Target の Build Phases で新しい Run Script Phase を追加し、shell に `/usr/local/bin/carthage copy-frameworks` を、Input Files に `$(SRCROOT)/Carthage/Build/iOS/TRETJapanNFCReader.framework` を記述する。<img width="1180" alt="スクリーンショット 2019-11-29 21.36.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/225d3f13-b5c7-8a48-2bdb-b7e734829dd8.png">
3. Project の Target の General で Frameworks, Libraries, and Embbedded Content で Carthage によって追加された `TRETJapanNFCReader.framework` を追加する。<img width="905" alt="スクリーンショット 2019-11-29 21.53.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/f3997e0d-c2af-bcbb-e2ed-440307d31bc8.png">


# Capability と Entitlements の設定
Project の Target から Signing & Capabilities を選択し、「+ Capability」から「Near Field Communication Tag Reading」を追加します。
<img width="1180" alt="スクリーンショット 2019-11-29 18.50.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/982c82d1-dad0-5a7d-a3c1-31f7ed31742b.png">
すると、「<Product-Name>.entitlements」というファイルが追加されているので、「Near Field Communication Tag Reader Session Formats」に「NFC Data Exchange Format」および「NFC tag-specific data protocol」があることを確認します。
<img width="1181" alt="スクリーンショット 2019-11-29 19.01.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/55989660-b3da-94d6-3459-1939e0da9011.png">

# Info.plist の設定
Info.plist に「Privacy - NFC Scan Usage Description」と「ISO7816 application identifiers for NFC Tag Reader Session」を追加します。
「Privacy - NFC Scan Usage Description」には NFC を何のために使用するのかについての説明を、「ISO7816 application identifiers for NFC Tag Reader Session」の配下には以下の AID を記述します。

- `A0000002310100000000000000000000`
- `A0000002310200000000000000000000`
- `A0000002480300000000000000000000`

<img width="909" alt="スクリーンショット 2019-11-29 20.39.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/3e407a7d-8c82-0ebd-e87c-41ecfddc64fe.png">

# コーディング
ではいよいよコーディングをしていきます。今回はすべて `ViewController.swift` に記述します。

```swift:ViewController.swift
import UIKit
import TRETJapanNFCReader // ①

class ViewController: UIViewController, DriversLicenseReaderSessionDelegate { // ②
    
    var reader: DriversLicenseReader! // ③
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.reader = DriversLicenseReader(viewController: self) // ④
        self.reader.get(items: DriversLicenseCardItem.allCases, pin1: "1234", pin2: "5678") // ⑤
    }
    
    func driversLicenseReaderSession(didRead driversLicenseCard: DriversLicenseCard) { // ②・⑥
        let matters = driversLicenseCard.matters!
        let registeredDomicile = driversLicenseCard.registeredDomicile!
        print("氏名", matters.name)
        print("呼び名(カナ)", matters.nickname)
        print("生年月日", matters.birthdate)
        print("本籍", registeredDomicile.registeredDomicile)
        print("住所", matters.address)
        print("交付年月日", matters.issuanceDate)
        print("照会番号", matters.referenceNumber)
        print("有効期間の末日", matters.expirationDate)
        print("免許の条件", matters.condition1, matters.condition2, matters.condition3, matters.condition4)
        print("公安委員会名", matters.issuingAuthority)
        print("免許証の番号", matters.number)
        print("普通免許の年月日", matters.ordinaryVehicleLicenceDate)
        print("中型免許の年月日", matters.mediumVehicleLicenceDate)
        
        // let image = UIImage(data: driversLicenseCard.photo!.photoData)
    }
    
    func japanNFCReaderSession(didInvalidateWithError error: Error) { // ②・⑥
        print(error.localizedDescription)
    }
}
```

1. ライブラリ `TRETJapanNFCReader` をインポートします。
2. `ViewController` に `DriversLicenseReaderSessionDelegate` を適用させます。必要な protocol stubs を追加します。
3. クラス内に `DriversLicenseReader`型の変数を用意します。
4. 3 で用意した変数を初期化します。
5. `reader.get` で運転免許証の読み取りに入ります。**`pin1`、`pin2`にはそれぞれ、運転免許証が交付されたときに自分が設定した暗証番号1、暗証番号2を指定します。絶対に間違えないでください（理由は後述）。**
6. 取得した運転免許証のデータを `print` します。

特に、5 の `reader.get` は以下のように定義されています。

```swift:
/// 運転免許証からデータを読み取る
/// - Parameter items: 運転免許証から読み取りたいデータ
/// - Parameter pin1: 暗証番号1
/// - Parameter pin2: 暗証番号2
func get(items: [DriversLicenseCardItem], pin1: String = "", pin2: String = "")
```

また、ここで指定する `DriversLicenseCardItem` は以下のように定義されています。

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

上記に挙げたサンプルでは `DriversLicenseCardItem.allCases` としてすべて指定していましたが、運転免許証の仕様上、「記載事項(本籍除く)」の読み取りには暗証番号1が、「記載事項(本籍)」、「写真」の読み取りには暗証番号1と暗証番号2の両方が必要です。**さらにその暗証番号は3回間違えると免許証がロックされ、警察署等でロック解除をしてもらわなければ運転免許証を読み取ることができなくなってしまいます。**読み取る際は慎重に試しましょう……。

例: 2回誤った暗証番号を入力 → 3回目で正しい暗証番号だった → 暗証番号試行回数は残り3回にリセットされる


# コンソールに表示される内容を確認する
それではいよいよ実機の iPhone で実行してみましょう。
暗証番号が正しい場合は情報がずらーっと表示されます。もし、暗証番号が間違っている場合は画面に残りの試行回数が表示されます。
<img width="480" alt="スクリーンショット 2019-11-29 22.17.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/734014db-343e-e9d5-8c30-65768146a3b4.png"> <img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/bf3139cf-ab3f-7d1e-1b0b-1e2eae644893.jpeg">


# まとめ
- **Core NFC で運転免許証を読み取ることができる**
- **暗証番号は間違ってはいけない**
- **実際の利用ケースとしては IDセルフィーの置き換え・補助になるかも**

## 今回のファイルツリー
![スクリーンショット 2019-11-29 22.23.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/7edafffe-1f7f-4067-8599-f4426c239efb.png)