気づけば 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 に追加します。
こんな感じで Item 0 に 0xFE00、Item 1 に 0x0003 を指定しました。
そして、NFCTagReaderSessionDelegate の tagReaderSession(_: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 を読み取ると……と、**ステータスフラグがエラー**……?
status1 が 1、status2 が 166 (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 システムコードの記述する順番に依存する
なぜこのような問題が起きたのでしょうか。
今回、switch で currentSystemCode が何かによって Suica か 楽天Edy かを判別しました。そして、0x0003 のみを持つ Apple Pay の Suica は currentSystemCode が 0x0003 であったため、無事に Suica と判別できましたが、カード型の Suica には 0x0003 の他に 0xFE00 も持っていたため、currentSystemCode が 0xFE00 となり 楽天Edyルートに入ることになってしまいました。
もし、FeliCa システムコードを用いてカードを判別しようとするとき、Info.plist に記述する FeliCa システムコードの順番が鍵となる場合があります。
いまは Item 0 に 0xFE00、Item 1 に 0x0003 を指定しました。この順番を逆にします。
この 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 に記載する順番が非常に重要になります。
取り上げたサンプルでは、FeliCa システムコードが2つだったので特に問題はありませんでしたが、上記スクリーンショットにある Item 4 の 0x8008 が検出されるまでには前4つ(0xFE00、0x0003、0x865E、0x8592)に比べて時間がかかります。
これが iOS 13 Core NFC で FeliCa を扱う上でまた足を引っ張ることに………。