12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Core NFCAdvent Calendar 2019

Day 19

FeliCa が遅いしフルスキャンも不可能 前編【iOS 13 Core NFC】

Last updated at Posted at 2019-12-18

気づけば Advent Calendar も後半戦…。
私はこれまで FeliCa を iOS 13 の Core NFC で読み取るサンプルを紹介してきました。

そして、前回の

では iOS での FeliCa の取り扱いの制限(システムの切り替えを IDm で行うことができない)について述べました。

…ですが、現在の iOS 13 での FeliCa に関する制限はまだまだあるのです…。

この記事で紹介する iOS での FeliCa の制限

まとめを先に書いておきます。

  • FeliCa の読み取りで得られる最初の currentSystemCode は Info.plist での FeliCa システムコードの記述する順番に依存する
  • FeliCa の読み取り速度は Info.plist での FeliCa システムコードの記述する順番に依存する
  • FeliCa のフルスキャンを iOS で行いたい場合、「じゃあ Info.plist に存在する全ての FeliCa システムコードを記載すればいいのでは…?」と考えつくが、それは現実的な解決策ではない後編につづく

FeliCa の読み取りで得られる最初の currentSystemCode は Info.plist での FeliCa システムコードの記述する順番に依存する

異なる FeliCa システムコードを持つカードを読み取ろうとしてみる

これまでに書いた「楽天Edy、nanaco、WAON」を読み取る方法と、「交通系IC(Suica、PASMO、ICOCA、…etc.)」を読み取る方法で、あらかじめ 使う FeliCa システムコードを Info.plist に追加しておく必要がある と記載してきました。楽天Edy、nanaco、WAON で使われている FeliCa システムコードは 0xFE00、交通系IC で主に使われている FeliCa システムコードは 0x0003 になります。

では、異なる FeliCa システムコードを持つ 楽天Edy と 交通系IC の残高を読み取るための iOS App を開発するにはどうすればいいでしょうか。
Capability と Entitlements の設定は済ませておくとして、まずは使う FeliCa システムコードを Info.plist に追加します。

スクリーンショット 2019-12-18 11.30.44.png

こんな感じで Item 00xFE00Item 10x0003 を指定しました。
そして、NFCTagReaderSessionDelegatetagReaderSession(_:didDetect:) を書いて検出された FeliCa タグに接続、データの読み取る準備をします。

func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
    let tag = tags.first!
    session.connect(to: tag) { (error) in
        if let error = error {
            session.invalidate(errorMessage: error.localizedDescription)
            return
        }
        
        guard case NFCTag.feliCa(let feliCaTag) = tag else {
            session.invalidate(errorMessage: "FeliCa ではない")
            return
        }
        
        session.alertMessage = "カードを読み取っています…"
        
        var serviceCode: Data!
        var startIndex = 0
        var endIndex = 0
        switch feliCaTag.currentSystemCode {
        case Data([0x00, 0x03]):
            // 交通系IC
            serviceCode = Data([0x00, 0x8B].reversed())
            startIndex = 11
            endIndex = 12
            break
        case Data([0xFE, 0x00]):
            // 楽天Edy
            serviceCode = Data([0x13, 0x17].reversed())
            startIndex = 0
            endIndex = 3
            break
        default:
            fatalError()
        }
        
        let blockList = [Data([0x80, 0x00])]
        feliCaTag.readWithoutEncryption(serviceCodeList: [serviceCode], blockList: blockList) { (status1, status2, blockData, error) in
            if let error = error {
                session.invalidate(errorMessage: error.localizedDescription)
                return
            }

            guard status1 == 0x00, status2 == 0x00 else {
                print("ステータスフラグがエラーを示しています", status1, status2)
                session.invalidate(errorMessage: "ステータスフラグがエラーを示しています s1:\(status1), s2:\(status2)")
                return
            }

            let data = blockData.first!
            let balance = data.toIntReversed(startIndex, endIndex)

            print(data as NSData)
            print("残高: ¥\(balance)")
            session.alertMessage = "残高: ¥\(balance)"
            session.invalidate()
        }
    }
}

こちらに示した tagReaderSession(_:didDetect:) 以外のコードは全て前々回の記事などと同じですので、そちらを参考にしてください。

検出された FeliCa カードの currentSystemCode が交通系ICのものか、楽天Edy のものかによって switch し、それぞれの serviceCode 等を指定しています。

ではビルドして実機で動作を確認してみましょう。

楽天Edy(カード型)を読み取った場合

残高が正しく表示されました。

Suica(Apple Pay)を読み取った場合

カード型でない、Suica が入っている Apple Watch に iPhone を近づけると…これも正しく残高を読み取ることができました。

Suica(カード型)を読み取った場合

では普通のカード型の Suica を読み取ると……と、**ステータスフラグがエラー**……?

status11status2166 (0xA6)というのは、"サービスコードリスト不正" にあたるので、サービスコードの指定が間違っていることになります。どうして Apple Pay の Suica は読み取れたのに、カードの Suica はダメだったんだろう…?

それぞれが持つ FeliCa システムコードを調べる

では、Japan NFC Reader を用いて Apple Pay の Suica とカード型の Suica が持つ FeliCa システムコードを調べてみましょう。
「その他」にある「IDm・PMm を読み取る」からカードをスキャンすると、入っている FeliCa システムコードを調べることができます。

楽天Edy(カード型)が持つ FeliCa システムコード

カード型の楽天Edyは `0x8B61` と `0xFE00` の2つの FeliCa システムコードを持っていることがわかります。

Apple Pay の Suica が持つ FeliCa システムコード

Apple Pay の Suica には確かに `0x0003` があることを確認できますね。

Suica(カード型)が持つ FeliCa システムコード

先ほど 読み取りに失敗したカード型の Suica は `0x0003` の他に… **`0xFE00` も持っている!?** [^1]

FeliCa の読み取りで得られる最初の currentSystemCode は Info.plist での FeliCa システムコードの記述する順番に依存する

なぜこのような問題が起きたのでしょうか。
今回、switchcurrentSystemCode が何かによって Suica か 楽天Edy かを判別しました。そして、0x0003 のみを持つ Apple Pay の Suica は currentSystemCode0x0003 であったため、無事に Suica と判別できましたが、カード型の Suica には 0x0003 の他に 0xFE00 も持っていたため、currentSystemCode0xFE00 となり 楽天Edyルートに入ることになってしまいました。

もし、FeliCa システムコードを用いてカードを判別しようとするとき、Info.plist に記述する FeliCa システムコードの順番が鍵となる場合があります。

スクリーンショット 2019-12-18 11.30.44.png

いまは Item 00xFE00Item 10x0003 を指定しました。この順番を逆にします。

スクリーンショット 2019-12-19 5.02.43.png

この Info.plist 以外はコードの変更を行わないまま、ビルドしてもう一度カード型の Suica をスキャンしてみましょう。

無事に正しい残高が表示されました!

FeliCa の読み取り速度は Info.plist での FeliCa システムコードの記述する順番に依存する

PaSoRi などでワイルドカードを使用して FeliCa カードに Polling をした場合、FeliCa に搭載されている 0 番目のシステムが検出されるかと思いますが、iOS 13 の Core NFC の場合は検出された FeliCa カードにあるシステムのうち、Info.plist に記載され 最も順番が前にある FeliCa システムコードに一致するシステム が検出、currentSystemCode および currentIDm にはそれが入ることになります。

そのため、多数の FeliCa システムコードに対応しようとした場合、Info.plist に記載する順番が非常に重要になります。

スクリーンショット 2019-12-19 5.21.09.png

取り上げたサンプルでは、FeliCa システムコードが2つだったので特に問題はありませんでしたが、上記スクリーンショットにある Item 4 の 0x8008 が検出されるまでには前4つ(0xFE000x00030x865E0x8592)に比べて時間がかかります。

これが iOS 13 Core NFC で FeliCa を扱う上でまた足を引っ張ることに………。

後編に続く

12
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?