【iOS】Core NFC応用 - ネイティブアクセス編
これまでの記事でNDEFメッセージの読み書きを行う方法について説明しました。
本記事では、iOS 13で追加された通信プロトコルでNFCタグにアクセスする方法について説明します。
Core NFCがカバーしているプロトコル
iOS 13以降では、NDEFメッセージの読み書きに加え、新たに4つの通信プロトコルを使用したNFCタグへのアクセスに対応しました。
- ISO7816
- ISO18092 (FeliCa)
- ISO14443 (MIFARE)
- ISO15693
新たに追加された通信プロトコルに準拠したNFCタグの検出
NDEFフォーマットに対応したNFCタグの検出はNFCNDEFReaderSession
を使用していましたが、新たに追加された通信プロトコルに準拠したNFCタグの検出は、NFCTagReaderSession
を使用します。
init(pollingOption:delegate:queue:)
でNFCTagReaderSession
オブジェクトを初期化します。pollingOption
引数には、NFCTagReaderSession.PollingOption
で通信プロトコルを指定します。NFCTagReaderSession.PollingOption
は以下のように定義されています。
他の引数にはNFCタグ検出時にコールされるデリゲートメソッドの委譲先、デリゲートメソッドがディスパッチされるキューを指定します。alertMessage
でNFCタグ検出処理が開始する際に表示されるハーフモーダルに表示するメッセージを指定します。
begin()
でセッションが開始し、NFCタグ検出処理が行われます。この時、iPhoneをNFCタグにかざすことを促すハーフモーダルが表示されます。
以降の章では、ISO18092 (FeliCa)プロトコルで、交通系ICカード (Suica)にアクセスしてIDmを取得する方法、ISO14443 (MIFARE)プロトコルで、NFCタグにアクセスする方法について説明します。
交通系ICカード (Suica)のIDmを取得する方法
info項目の編集
info項目一覧のKeyに「ISO18092 system codes for NFC Tag Reader Session」を追加し、Arrayの要素にString型で「0003」を追加します。
この「0003」は「システムコード」と呼ばれ、企業 / 団体毎あるいはアプリケーション毎に割り当てられている認証コードです。
交通系ICカード (Suica)の検出
init(pollingOption:delegate:queue:)
でNFCTagReaderSession
オブジェクトを初期化します。pollingOption
引数には、検出するNFCタグが準拠するプロトコルを表すNFCTagReaderSession.PollingOption
、NFCタグ検出時にコールされるデリゲートメソッドの委譲先、デリゲートメソッドがディスパッチされるキューを指定します。
以下のコードでは、ISO18092 (FeliCa)プロトコルに準拠したNFCタグを検出するためのNFCTagReaderSession
オブジェクトを生成しています。
交通系ICカード (Suica)を検出する時は、pollingOption
に.iso18092
を指定します。
NFCTagReaderSessionDelegate
プロトコル
NFCTagReaderSession
のイニシャライザで指定した委譲先のクラスを、NFCTagReaderSessionDelegate
プロトコルに準拠させます。NFCTagReaderSessionDelegate
プロトコルには、NFCタグ検出時の様々なリーダーセッションイベントを処理するためのデリゲートメソッドが定義されています。
NFCタグ検出後の処理を実装する前に、NFCTagReaderSessionDelegate
プロトコルで定義されているデリゲートメソッドを確認しましょう。
tagReaderSessionDidBecomeActive(_:)
tagReaderSession(_:didDetect:)
tagReaderSession(_:didInvalidateWithError:)
tagReaderSessionDidBecomeActive(_:)
リーダーセッションがアクティブになった時にコールされるメソッドです。
tagReaderSession(_:didDetect:)
リーダーセッションがNFCタグを検出した時にコールされるメソッドです。このメソッド内でNFCタグに書き込まれているデータの読み取り/書き込み機能を実装します。
tagReaderSession(_:didInvalidateWithError:)
リーダーセッションが無効になった時にコールされるメソッドです。何らかのエラーが発生し、リーダーセッションが中断された時や完了した時にコールされます。
タグをリーダーセッションに接続
NFCTagReaderSession
のconnect(to:completionHandler:)
でタグをリーダーセッションに接続します。
タグの取得
NFCTag
オブジェクトを利用してNFCFeliCaTag
オブジェクトを取得します。
NFCFeliCaTag
オブジェクトのcurrentIDm
でFeliCaのIDmを取得することができます。
ISO14443 (MIFARE)プロトコルでNFCタグにアクセスする方法
init(pollingOption:delegate:queue:)
でNFCTagReaderSession
オブジェクトを初期化します。pollingOption
引数には、検出するNFCタグが準拠するプロトコルを表すNFCTagReaderSession.PollingOption
、NFCタグ検出時にコールされるデリゲートメソッドの委譲先、デリゲートメソッドがディスパッチされるキューを指定します。
以下のコードでは、ISO14443 (MIFARE)プロトコルに準拠したNFCタグを検出するためのNFCTagReaderSession
オブジェクトを生成しています。
ISO14443 (MIFARE)プロトコルに準拠したNFCタグを検出する時は、pollingOption
に.iso14443
を指定します。
タグの取得
NFCTag
オブジェクトを利用してNFCMiFareTag
オブジェクトを取得します。MIFAREはいくつかの規格が存在しており、それぞれメモリ構成、セキュリティ面、価格が異なります。以下のコードでは、UltralightタイプのNFCMiFareTag
オブジェクトを取得しています。
タグをリーダーセッションに接続
NFCTagReaderSession
のconnect(to:completionHandler:)
でタグをリーダーセッションに接続します。
MIFAREプロトコルでタグにアクセス
MIFAREプロトコルでタグにアクセスするためには、sendMiFareCommand(commandPacket:completionHandler:)
を使用します。commandPacket
には読み込み用または書き込み用のコマンドを指定します。
データの読み込み
読み込み用のコマンド
読み込み用のコマンドは0x30
です。第1要素は0x30
、第2要素は読み込み先ブロックのアドレスを格納したUInt8型を要素とする配列を、Data
型でキャストしたものを用意します。例えば、以下のコマンドは5ブロック目のデータを読み込むコマンドになります。
Data([0x30, 0x05])
用意したコマンドを、sendMiFareCommand(commandPacket:completionHandler:)
のcommandPacket
引数に指定します。例えば、以下を実行すると、5ブロック目のデータを読み込む処理が実行されます。
sendMiFareCommand(commandPacket: Data([0x30, 0x05]))
読み込み処理
NFCMiFareTag
オブジェクトを取得した後、sendMiFareCommand(commandPacket:completionHandler:)
を実行して読み込んだデータを表すData
オブジェクトを取得します。取得したData
オブジェクトをString
オブジェクトに変換することで、NFCタグに書き込まれていた文字列を取得することができます。
データの書き込み
書き込み用のコマンド
書き込み用の固定コマンドは0xA2
です。第1要素は0xA2
、第2要素は書き込み先ブロックのアドレスを格納したUInt8型を要素とする配列を、Data型でキャストしたものに対して、書き込みたいデータを表すData
型オブジェクトを加えたものを用意します。例えば、以下のコマンドは5ブロック目にData
型のwriteData
を書き込むコマンドになります。
Data([0xA2, 0x05]) + writeData
用意したコマンドを、sendMiFareCommand(commandPacket:completionHandler:)
のcommandPacket
引数に指定します。例えば、以下を実行すると、5ブロック目にデータを書き込む処理が実行されます。
sendMiFareCommand(commandPacket: Data([0xA2, 0x05]) + writeData)
書き込みデータの準備
NFCタグに書き込みたい文字列をData
型オブジェクトにキャストします。sendMiFareCommand(commandPacket:completionHandler:)
は1ブロックずつ書き込み処理を行うため、NFCタグに書き込むData
型オブジェクトblockData
のサイズは1ブロックサイズにする必要があります。MIFARE - Ultralightタイプのタグは、1ブロック4byteとなっているため、blockData
のサイズも4byteにします。
サンプルコードでは、String
型のwriteMesage
のうち先頭4文字分をData
型にキャストすることで、4byteのData
型オブジェクトblockData
を生成しています。
もし、writeMesage
に格納されている文字列が4文字未満 (4byte未満)だった時、空のData
型オブジェクトをblockData
に追加することで、blockData
のサイズを4byteにしています。
書き込み処理
書き込みコマンドを、sendMiFareCommand(commandPacket:completionHandler:)
のcommandPacket
引数に指定して書き込み処理を行います。書き込みに成功した時、返ってきたData
型オブジェクトの1byte目に0x0A
が格納されています。
今回は4byte文字列の読み書きを行いました。4byteよりも大きなサイズのデータを読み書きを行う時は、複数ブロックに跨ってデータの読み書きを行います。また、MIFARE - Ultralightタイプのタグは1ブロックあたりのサイズが4byteですが、1ブロックあたりのサイズが16byteのものもあります。MIFAREプロトコルでデータの読み書きを行う時は、使用するタグのタイプをチェックするようにしましょう。
サンプルソース
ISO18092 (FeliCa)
ISO14443 (MIFARE)
参考資料
・Core NFC Enhancements
・What's new in Core NFC
Discussion