core-nfc について
Core NFCを使ったNFCタグの読み書きを扱う、iPhoneアプリ開発者向けスキルです。NDEFメッセージ、タグ固有のプロトコル、機能設定、バックグラウンド読み取りを解説します。
主な機能
- 01NDEFタグの内容を読み取る
- 02ISO 7816、ISO 15693、FeliCa、MIFAREタグの読み書き
- 03NDEFメッセージの作成と解析
- 04NFCアクセスとセッションの設定
- 05URIタグによるバックグラウンド読み取り
活用シーン
- 01タグに記録されたURLやテキストの読み取り
- 02書き込み可能なタグへのNDEFメッセージ書き込み
- 03特定プロトコルを使ったタグ操作
- 04ユニバーサルリンクによるタグからのアプリ起動
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill core-nfcインストール後、Claude Code で/core-nfcを入力して読み込まれたか確認します。
同梱ファイルevals/evals.jsonreferences/nfc-patterns.md
詳細
CoreNFC
CoreNFC フレームワークを使用して、iPhone で NFC タグを読み書きします。NDEF リーダーセッション、タグリーダーセッション、NDEF メッセージの構築、エンタイトルメント、バックグラウンドでのタグ読み取りについて説明します。
目次
- セットアップ
- NDEF リーダーセッション
- タグリーダーセッション
- NDEF メッセージの書き込み
- NDEF ペイロードの種類
- バックグラウンドでのタグ読み取り
- よくある間違い
- 確認チェックリスト
- 参考資料
セットアップ
プロジェクトの設定
- Xcode で Near Field Communication Tag Reading ケイパビリティを追加します
- Info.plist に
NFCReaderUsageDescriptionを追加し、ユーザー向けの理由を記述します - 現行の
TAG値を指定したcom.apple.developer.nfc.readersession.formatsエンタイトルメントを追加します。レガシーなNDEFは追加しないでください - ISO 7816 タグの場合、Info.plist の
com.apple.developer.nfc.readersession.iso7816.select-identifiersに対応するアプリケーション識別子を追加します - FeliCa タグの場合、
com.apple.developer.nfc.readersession.felica.systemcodesに対応するシステムコードを追加します。ワイルドカードのシステムコードは使用しないでください
デバイス要件
NFC の読み取りには iPhone 7 以降が必要です。NFC の UI やセッションを作成する前に、リーダーセッションが利用可能か必ず確認してください。これから作成する具体的なリーダーセッションの種類を使用してください。
import CoreNFC
guard NFCNDEFReaderSession.readingAvailable else {
// Device does not support NFC or feature is restricted
showUnsupportedMessage()
return
}
主な型
| 型 | 役割 |
|---|---|
NFCNDEFReaderSession | NDEF 形式のタグをスキャンします |
NFCTagReaderSession | ISO7816、ISO15693、FeliCa、MIFARE タグをスキャンします |
NFCNDEFMessage | NDEF ペイロードレコードのコレクションです |
NFCNDEFPayload | NDEF メッセージ内の単一レコードです |
NFCNDEFTag | NDEF 対応タグとやり取りするためのプロトコルです |
NDEF リーダーセッション
タグから NDEF 形式のデータを読み取るには、NFCNDEFReaderSession を使用します。URL、テキスト、MIME データなど、標準的なタグの内容を読み取る最も簡単な方法です。
import CoreNFC
final class NDEFReader: NSObject, NFCNDEFReaderSessionDelegate {
private var session: NFCNDEFReaderSession?
func beginScanning() {
guard NFCNDEFReaderSession.readingAvailable else { return }
session = NFCNDEFReaderSession(
delegate: self,
queue: nil,
invalidateAfterFirstRead: false
)
session?.alertMessage = "Hold your iPhone near an NFC tag."
session?.begin()
}
// MARK: - NFCNDEFReaderSessionDelegate
func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
// Session is scanning
}
func readerSession(
_ session: NFCNDEFReaderSession,
didDetectNDEFs messages: [NFCNDEFMessage]
) {
for message in messages {
for record in message.records {
processRecord(record)
}
}
}
func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
let nfcError = error as? NFCReaderError
if nfcError?.code != .readerSessionInvalidationErrorFirstNDEFTagRead,
nfcError?.code != .readerSessionInvalidationErrorUserCanceled {
print("Session invalidated: \(error.localizedDescription)")
}
self.session = nil
}
}
タグ接続による読み取り
読み書き操作では、タグ検出デリゲートメソッドを使用して、個々のタグに接続します。
func readerSession(
_ session: NFCNDEFReaderSession,
didDetect tags: [any NFCNDEFTag]
) {
guard let tag = tags.first else {
session.restartPolling()
return
}
session.connect(to: tag) { error in
if let error {
session.invalidate(errorMessage: "Connection failed: \(error)")
return
}
tag.queryNDEFStatus { status, capacity, error in
guard error == nil else {
session.invalidate(errorMessage: "Query failed.")
return
}
switch status {
case .notSupported:
session.invalidate(errorMessage: "Tag is not NDEF compliant.")
case .readOnly:
tag.readNDEF { message, error in
if let message {
self.processMessage(message)
}
session.invalidate()
}
case .readWrite:
tag.readNDEF { message, error in
if let message {
self.processMessage(message)
}
session.alertMessage = "Tag read successfully."
session.invalidate()
}
@unknown default:
session.invalidate()
}
}
}
}
タグリーダーセッション
ネイティブのタグプロトコル(ISO 7816、ISO 15693、FeliCa、MIFARE)に直接アクセスする必要がある場合は、NFCTagReaderSession を使用します。
| ポーリングオプション | タグ |
|---|---|
.iso14443 | ISO 7816 互換タグおよび MIFARE |
.iso15693 | ISO 15693 |
.iso18092 | FeliCa |
決済関連の AID には、このセッションを使用しないでください。プロトコル固有の接続、APDU、コマンド、レスポンスの処理については、nfc-patterns.md を読み込んでください。
NDEF メッセージの書き込み
接続したタグに NDEF データを書き込みます。必ず最初に readWrite の状態を確認してください。
func writeToTag(
tag: any NFCNDEFTag,
session: NFCNDEFReaderSession,
url: URL
) {
tag.queryNDEFStatus { status, capacity, error in
guard status == .readWrite else {
session.invalidate(errorMessage: "Tag is read-only.")
return
}
guard let payload = NFCNDEFPayload.wellKnownTypeURIPayload(
url: url
) else {
session.invalidate(errorMessage: "Invalid URL.")
return
}
let message = NFCNDEFMessage(records: [payload])
tag.writeNDEF(message) { error in
if let error {
session.invalidate(
errorMessage: "Write failed: \(error.localizedDescription)"
)
} else {
session.alertMessage = "Tag written successfully."
session.invalidate()
}
}
}
}
NDEF ペイロードの種類
一般的なペイロードの作成
// URL payload
let urlPayload = NFCNDEFPayload.wellKnownTypeURIPayload(
url: URL(string: "https://example.com")!
)
// Text payload
let textPayload = NFCNDEFPayload.wellKnownTypeTextPayload(
string: "Hello NFC",
locale: Locale(identifier: "en")
)
// Custom payload
let customPayload = NFCNDEFPayload(
format: .nfcExternal,
type: "com.example:mytype".data(using: .utf8)!,
identifier: Data(),
payload: "custom-data".data(using: .utf8)!
)
ペイロードの内容の解析
完全な type-name-format の switch 文と複数レコードの処理については、NDEF ペイロードの内容の解析 を読み込んでください。
バックグラウンドでのタグ読み取り
iPhone XS 以降では、アプリを開かなくても iOS がバックグラウンドで NFC タグを読み取れます。NDEF メッセージには URI レコード(typeNameFormat == .nfcWellKnown、type U)が含まれている必要があります。URI レコードが複数ある場合、システムは最初のレコードを使用します。
アプリ固有のルーティングには、タグにユニバーサルリンクを書き込み、そのドメインに対して Associated Domains ケイパビリティを設定します。バックグラウンドでのタグ読み取りは、Web、メール、SMS、電話、FaceTime、Maps、HomeKit の設定など、特定のシステム URL スキームにも対応しています。カスタム URL スキームには対応しておらず、システムはバンドル ID や任意の NDEF コンテンツタイプに基づいてルーティングしません。
ユーザーが対応するタグにタップすると、iOS はアプリを開く通知を表示します。NSUserActivity を使用してタグデータを処理します。
func scene(
_ scene: UIScene,
continue userActivity: NSUserActivity
) {
guard userActivity.activityType ==
NSUserActivityTypeBrowsingWeb else { return }
let message = userActivity.ndefMessagePayload
guard message.records.first?.typeNameFormat != .empty else { return }
for record in message.records {
processRecord(record)
}
}
よくある間違い
避ける:古い、または不足している NFC エンタイトルメントを使用する
com.apple.developer.nfc.readersession.formats エンタイトルメントがないと、リーダーセッションから NFC ハードウェアにアクセスできません。Core NFC のリーダーセッションには現行の TAG 値を使用してください。NDEF を追加する古い例をそのままコピーしないでください
しないこと:セッション無効化エラーを無視する
セッションはさまざまな理由で無効になります。ユーザーによるキャンセルと実際のエラーを区別することで、誤ったエラー通知を防げます。
// WRONG -- shows error when user cancels
func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
showAlert("NFC Error: \(error.localizedDescription)")
}
// CORRECT -- filter expected invalidation reasons
func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
let nfcError = error as? NFCReaderError
switch nfcError?.code {
case .readerSessionInvalidationErrorUserCanceled,
.readerSessionInvalidationErrorFirstNDEFTagRead:
break // Normal termination
default:
showAlert("NFC Error: \(error.localizedDescription)")
}
self.session = nil
}
しないこと:無効になったセッションへの強い参照を保持する
セッションは無効になると、再開できません。参照を nil にし、次回のスキャン時に新しいセッションを作成してください。
// WRONG -- reusing invalidated session
func scanAgain() {
session?.begin() // Does nothing, session is dead
}
// CORRECT -- create a new session
func scanAgain() {
session = NFCNDEFReaderSession(
delegate: self, queue: nil, invalidateAfterFirstRead: false
)
session?.begin()
}
レビューチェックリスト
- Signing & Capabilities で NFC 機能を追加している
-
NFCReaderUsageDescriptionを Info.plist に設定している -
com.apple.developer.nfc.readersession.formatsエンタイトルメントで、旧式のNDEFではなくTAGを使用している - セッションを作成する前に
NFCNDEFReaderSession.readingAvailableまたはNFCTagReaderSession.readingAvailableを確認している -
begin()の呼び出し前にセッションのデリゲートを設定している - 無効化後にセッションへの参照を nil にしている
-
didInvalidateWithErrorでユーザーによるキャンセルと実際のエラーを区別している - 書き込み操作の前に NDEF ステータスを問い合わせている
- 大きなメッセージを書き込む前にタグの容量を確認している
-
NFCTagReaderSessionを使用する場合、ISO 7816 アプリケーション識別子を Info.plist に記載している -
.iso18092のポーリング時に FeliCa システムコードを Info.plist に記載している - バックグラウンドでのタグ読み取りに URI NDEF レコードとユニバーサルリンク、またはサポートされているシステム URL スキームを使用している
- バックグラウンドでのルーティングにカスタム URL スキーム、バンドル ID、任意の NDEF コンテンツタイプを使用していない
- 決済関連の AID を
NFCTagReaderSession以外にルーティングしている - 一度にアクティブにするリーダーセッションを 1 つだけにしている
参考資料
- 拡張パターン(ISO 7816 コマンド、複数タグのスキャン、NDEF のロック):references/nfc-patterns.md
- Core NFC フレームワーク
- NFCNDEFReaderSession
- NFCTagReaderSession
- NFCNDEFMessage
- NFCNDEFPayload
- NFCNDEFTag
- NFCNDEFReaderSessionDelegate
- NFCTagReaderSessionDelegate
- NFC タグリーダーアプリの構築
- バックグラウンドでのタグ読み取りへの対応の追加
- 近距離無線通信タグリーダーセッション形式エンタイトルメント
- NFC タグリーダーセッション用の ISO7816 アプリケーション識別子
- NFC タグリーダーセッション用の ISO18092 システムコード