気づけば 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 を扱う上でまた足を引っ張ることに………。