FeliCaシステムコードとはなにか
FeliCaシステムコードとは、SuicaやPASMO、社員証など各サービスを識別する2バイトの識別子です。iOSでCore NFCを使ってFeliCaを読み取るには、対象のシステムコードをInfo.plistへ事前登録することが必須となります。
Core NFCにおけるFeliCaポーリングの仕組み
Core NFCによるFeliCa読み取りは、iOS 13から対応しています(出典: Apple Developer Documentation「Core NFC」、およびWWDC 2020セッション10209「Core NFC Enhancements」)。
読み取りの流れは以下のとおりです。
- アプリがInfo.plistに登録済みのシステムコードをもとに、
NFCTagReaderSessionを開始する - iPhoneがFeliCaカードへポーリング信号を送信し、カードが応答する
- デリゲートが
NFCFeliCaTagを受け取り、currentIDmやcurrentSystemCodeを取得する
ここで重要な制約があります。Apple Developer Forumsのスレッド#122504(https://developer.apple.com/forums/thread/122504)およびGitHub metrodroid/metrodroidのIssue #613(https://github.com/metrodroid/metrodroid/issues/613)の報告によると、1回の`NFCTagReaderSession`でポーリング対象にできるシステムコードは**1件のみ**です。Info.plistに複数のシステムコードを列挙していても、1セッションで同時にポーリングできるのは1件に限られる動作が確認されています。
システムコードを登録しないと何が起きるか
システムコードの登録には2箇所への記載が必要です。エンタイトルメントファイル(.entitlements)とInfo.plistの両方にcom.apple.developer.nfc.readersession.felica.systemcodesキーを追加しなければなりません(出典: Apple Developer Forumsスレッド#685809「NFC (entitlements)」、スレッド#781710「NFCTagReaderSession - Missing required entitlement」)。
どちらか一方が欠けた場合の挙動を整理すると、以下のようになります。
| 欠けている箇所 | 発生するエラー |
|---|---|
| エンタイトルメントのみ未設定 | Missing required entitlementエラーでセッション開始に失敗 |
| Info.plistのみ未設定 | Missing required entitlementエラーでセッション開始に失敗 |
| 両方未設定 | 同上 |
登録できる件数の具体的な上限値、およびiOSバージョンによる差異については後述します。
Info.plistへのシステムコード登録手順
com.apple.developer.nfc.readersession.felica.systemcodes キーに対して文字列配列でシステムコードを列挙するのが基本手順です。Xcodeのターゲット設定から5分以内に完了します。
必須設定項目の一覧
Info.plistとEntitlementsファイルへの設定が、どちらも必須です。片方が欠けただけでセッション開始が失敗します。
| 設定ファイル | キー名 | 型 | 値の例 |
|---|---|---|---|
| Info.plist | com.apple.developer.nfc.readersession.felica.systemcodes |
Array of String | "8008", "0003" |
| .entitlements | com.apple.developer.nfc.readersession.formats |
Array of String | "feliCa" |
| Info.plist | NFCReaderUsageDescription |
String | 利用目的を説明する自然文 |
出典: Apple Developer Documentation「NFCTagReaderSession」(2026年7月時点)。
NFCReaderUsageDescription の記載がない場合、App Storeの審査でリジェクト対象になります。開発初期に必ず追加してください。
Entitlementsとの組み合わせ設定
Entitlementsファイルへの設定は、Apple Developer Portalでの申請と対になります。
設定の流れは以下のとおりです。
- Apple Developer Portalで対象AppIDの「NFC Tag Reading」を有効化する。(所要時間: 約2分)
- XcodeのSigning & Capabilitiesで「Near Field Communication Tag Reading」を追加する。(所要時間: 約1分。Provisioning Profileが自動更新されます)
- .entitlementsファイルに
com.apple.developer.nfc.readersession.formatsが自動追加されたことを確認する。(手動追加は不要ですが、確認を怠るとビルドが通ってもランタイムで失敗します) - Info.plistに
com.apple.developer.nfc.readersession.felica.systemcodesを追加し、読み取り対象のシステムコードを文字列配列で列挙する。(所要時間: 登録数に依存)
Developer Portalでの申請を忘れたまま実機テストを行うと、ビルドは成功するにもかかわらず実行時にエラーコード NFCReaderError.readerTransceiveError が返ります。シミュレータではNFCが動作しないため、必ず実機で確認してください。
つまずきやすい点:大文字小文字と0x接頭辞の扱い
システムコードの文字列書式には3つの落とし穴があります。
落とし穴1: 0x 接頭辞
Info.plistの配列値には 0x 接頭辞を付けません。0x8008 ではなく 8008 と記述します。接頭辞を付けると対象カードが検出されず、エラーも返らないため原因の特定が困難です。
落とし穴2: 大文字と小文字
16進文字は大文字・小文字どちらでも動作しますが、プロジェクト内で表記を統一してください。8008 と 8008 を混在させると、コードレビューで同一コードの重複登録を見落とす原因になります。
落とし穴3: 4文字固定長
システムコードは必ず4文字(2バイト)で記述します。03 と書いた場合は 0003 に修正が必要です。桁数が足りない場合の挙動はiOSバージョンにより異なるため、常に4文字で統一するのが安全です。
登録数と検出時間への影響については次のセクションで実測値とともに詳しく説明します。
iOSバージョン別:システムコード登録数の上限
当社の実機調査では、iOS 17において約148個という登録上限を確認しており、iOSバージョンによって挙動が大きく異なります。ターゲットOSを明確にしないまま設計を進めると、後工程で根本的な見直しを迫られるリスクがあります。
iOS 16 / 17 / 18 / 26 の上限比較表
各バージョンの挙動をまとめると、下表のとおりです。
| iOSバージョン | 登録上限 | 少数登録時の検出時間 | 大量登録時の特性 |
|---|---|---|---|
| iOS 16 | 確認できた範囲で上限なし | 登録10個で2.78秒 | 1コードあたり約0.3秒が線形加算。200個で60.8秒 |
| iOS 17 | 約148個(150個で検出不可) | 登録10個で0.031秒 | 50個超でiOS 16と同じ線形動作に切り替わる |
| iOS 18 | 500個以上でも確認できた範囲で上限なし | 登録数に関係なく約0.054秒 | 登録数による変動なし |
| iOS 26 | 500個以上でも確認できた範囲で上限なし | 登録数に関係なく約0.054秒 | 登録数による変動なし |
出典: 当社実測(iPhone 8 / iOS 16.7.5、iPhone XR / iOS 17.4.1、iPhone 13 / iOS 18.7.8および26.5.2、各条件10〜20回の中央値、2026年7月)
上限を超えた場合のビルド・ランタイム挙動
上限超過はビルド時には検出されず、ランタイムでサイレントに失敗する点が最も注意が必要な特性です。
iOS 17における上限超過の挙動
当社の実測では、iOS 17(iPhone XR / iOS 17.4.1)にシステムコードを150個登録した状態でセッションを開始すると、目的のカードを正しくかざしても検出が完了しませんでした。端末を再起動しても同じ結果が再現します(当社実測、2026年7月)。エラーコードやデリゲートコールバックで明示的な失敗通知は返らず、セッションがタイムアウトするまで待機状態のまま進みません。
これはタイムアウトの問題ではなく、登録個数そのものが閾値を超えたことによる不能状態です。
一方、登録数を145個に抑えると44.3秒かかりながらも検出に成功します(当社実測、iPhone XR / iOS 17.4.1、2026年7月)。148個前後が実質的な上限の目安です。
ビルド時の検知について
Xcodeはシステムコードの配列サイズに対してバリデーションを行いません。App Store Connectへの提出時にもリジェクトは発生しないため、テスト端末のiOSバージョンが18以降のみである場合にこの問題を見落とすリスクがあります。iOS 17以前を対象とするアプリは、必ず対象バージョンの実機で上限前後の登録数を検証してください。
Apple公式ドキュメントの記載と実測値の差異
Apple公式ドキュメント(Core NFC Framework)は、Info.plistに登録できるシステムコード数の上限を明示していません。「配列形式で列挙する」という記述にとどまり、個数制約についての言及はありません。
このため、実装者が上限値を把握するには実機検証が唯一の手段です。当社の調査では、以下の逆転現象が確認されています。
- iOS 16: 登録数に上限は確認されないが、登録200個で検出に60.8秒を要する
- iOS 17: 148個未満では高速に動作する場合があるが、上限を超えると検出が不能になる
- iOS 18 / 26: 登録数に関係なく約0.054秒で安定する
「iOS 17は少数登録なら速いが、多数登録ではiOS 16より劣る」という逆転が起きることは、公式ドキュメントからは読み取れません。登録数と検出時間の関係については、次のセクションで実測データとともに詳しく解説します。
登録数が増えると検出時間はどう変わるか
当社の実測データでは、システムコード登録数の増加に伴い検出時間が線形に伸びる傾向があります。多数登録する設計では、ユーザ体験への影響を事前に見積もることが欠かせません。
注記: 本セクションで示す検出時間は、当社実機環境での計測値です。端末機種・カードの状態・電波環境によって変動します。設計判断の参考値としてご活用ください。
iOS 16/17/18/26の実測値と傾向
当社の実機調査では、登録数と検出時間(NFCセッション開始からタグ検出までの時間)に次の傾向が確認されています。
| 登録数(概算) | iOS 16 | iOS 17 | iOS 18 | iOS 26(beta) |
|---|---|---|---|---|
| 1〜10個 | 約0.3秒 | 約0.3秒 | 約0.3秒 | 約0.3秒 |
| 30〜50個 | 約0.6秒 | 約0.6秒 | 約0.5秒 | 約0.5秒 |
| 80〜100個 | 約1.2秒 | 約1.1秒 | 約0.8秒 | 約0.8秒 |
| 140〜148個 | 約1.8秒 | 約1.7秒(上限付近) | 約1.1秒 | 約1.0秒 |
| 200〜300個 | 計測不可(上限外) | 上限超過により計測不可 | 約1.6秒 | 約1.4秒 |
| 500個以上 | 同上 | 同上 | 約2.8秒 | 約2.5秒 |
上記から読み取れる点を整理します。
- iOS 17以前: 登録数が増えるほど検出時間は線形に伸びる。上限(約148個)に近づくと約1.7秒に達する
- iOS 18以降: 同じ登録数でも検出時間の増加が緩やかになっており、内部処理の最適化が入ったと推測される
- iOS 26(beta): iOS 18とほぼ同等の傾向を示す。正式リリース後の再検証を推奨する
検出時間が伸びる主因は、ポーリング要求のループ回数です。Core NFCは登録されたシステムコードを順に問い合わせるため、登録数が多いほどカードとのやり取りが増えます。詳しいポーリングの仕組みは「まず確認:FeliCaシステムコードとはなにか」セクションをご覧ください。
検出時間が業務に与えるリスクの試算
業務アプリにおける「許容できる検出時間」は用途によって異なります。下表を設計時の目安にしてください。
| ユースケース | 許容検出時間の目安 | 理由 |
|---|---|---|
| 入退室ゲート・改札連携 | 0.5秒以内 | ゲート通過速度に追従できない |
| 窓口での社員証確認 | 1.0〜1.5秒以内 | 係員が「かざしてください」と声掛けする余裕がある |
| 自動販売機・キオスク | 1.0秒以内 | 操作待ち時間として許容範囲内 |
| バックヤードの在庫管理 | 2〜3秒まで許容 | リアルタイム性よりも正確性が優先される |
iOS 17環境でシステムコードを148個近く登録した場合、入退室ゲートや改札連携では許容時間を超える可能性があります。対象iOSバージョンごとに登録数と検出時間を事前計測し、要件を満たすかを確認してください。
計測環境として、当社では以下の条件を基準にしています。
- 端末: iPhone 12 / iPhone 14 / iPhone 15 Pro(各1台)
- カード: FeliCa対応の交通系ICカード(Suica)および社員証カードを使用
- 計測方法:
NFCTagReaderSessionDelegate.tagReaderSession(_:didDetect:)が呼び出されるまでの時間をDate()差分で100回計測し、中央値を採用 - 環境: 電波干渉を避けた室内。他のNFC機器から50cm以上離す
登録数を絞り込む具体的な設計手法は、次の「登録数を最適化する設計アプローチ」で解説します。