使用 CoreNFC 讀寫 NFC 標籤。適用於掃描 NDEF 標籤、讀取 ISO7816/ISO15693/FeliCa/MIFARE 標籤、寫入 NDEF 訊息、處理 NFC 會話生命週期、設定 NFC 授權,或在 iOS App 中實作背景標籤讀取。
CoreNFC
在 iPhone 上使用 CoreNFC 框架讀寫 NFC 標籤。涵蓋 NDEF 讀取器會話、標籤讀取器會話、NDEF 訊息建構、授權設定以及背景標籤讀取。
目錄
設定
專案設定
- 在 Xcode 中新增 Near Field Communication Tag Reading 功能
- 在 Info.plist 中加入
NFCReaderUsageDescription,並提供給使用者看的理由字串 - 加入
com.apple.developer.nfc.readersession.formats授權,並使用目前的TAG值;不要加入舊版的NDEF - 若使用 ISO 7816 標籤,在 Info.plist 的
com.apple.developer.nfc.readersession.iso7816.select-identifiers中加入支援的應用程式識別碼 - 若使用 FeliCa 標籤,在 Info.plist 的
com.apple.developer.nfc.readersession.felica.systemcodes中加入支援的系統代碼;不要使用萬用字元系統代碼
裝置需求
NFC 讀取需要 iPhone 7 或後續機型。在建立 NFC UI 或會話之前,務必檢查讀取器會話是否可用。使用你即將建立的具體讀取器會話類型。
import CoreNFC
guard NFCNDEFReaderSession.readingAvailable else {
// 裝置不支援 NFC 或功能被限制
showUnsupportedMessage()
return
}
關鍵類型
| 類型 | 角色 |
|---|---|
NFCNDEFReaderSession |
掃描 NDEF 格式的標籤 |
NFCTagReaderSession |
掃描 ISO7816、ISO15693、FeliCa、MIFARE 標籤 |
NFCNDEFMessage |
NDEF 承載記錄的集合 |
NFCNDEFPayload |
NDEF 訊息中的單一記錄 |
NFCNDEFTag |
與支援 NDEF 的標籤互動的通訊協定 |
NDEF 讀取器會話
使用 NFCNDEFReaderSession 從標籤讀取 NDEF 格式的資料。這是讀取標準標籤內容(如 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 = "將你的 iPhone 靠近 NFC 標籤。"
session?.begin()
}
// MARK: - NFCNDEFReaderSessionDelegate
func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
// 會話正在掃描
}
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("會話已失效:\(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: "連線失敗:\(error)")
return
}
tag.queryNDEFStatus { status, capacity, error in
guard error == nil else {
session.invalidate(errorMessage: "查詢失敗。")
return
}
switch status {
case .notSupported:
session.invalidate(errorMessage: "標籤不符合 NDEF 規範。")
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 = "標籤讀取成功。"
session.invalidate()
}
@unknown default:
session.invalidate()
}
}
}
}
標籤讀取器會話
當你需要直接存取原生標籤通訊協定(ISO 7816、ISO 15693、FeliCa 或 MIFARE)時,使用 NFCTagReaderSession。
| 輪詢選項 | 標籤 |
|---|---|
.iso14443 |
ISO 7816 相容及 MIFARE |
.iso15693 |
ISO 15693 |
.iso18092 |
FeliCa |
不要將此會話用於與支付相關的 AID。請載入 nfc-patterns.md 以取得協定特定的連線、APDU、指令和回應處理。
寫入 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: "標籤為唯讀。")
return
}
guard let payload = NFCNDEFPayload.wellKnownTypeURIPayload(
url: url
) else {
session.invalidate(errorMessage: "無效的 URL。")
return
}
let message = NFCNDEFMessage(records: [payload])
tag.writeNDEF(message) { error in
if let error {
session.invalidate(
errorMessage: "寫入失敗:\(error.localizedDescription)"
)
} else {
session.alertMessage = "標籤寫入成功。"
session.invalidate()
}
}
}
}
NDEF 承載類型
建立常見承載
// URL 承載
let urlPayload = NFCNDEFPayload.wellKnownTypeURIPayload(
url: URL(string: "https://example.com")!
)
// 文字承載
let textPayload = NFCNDEFPayload.wellKnownTypeTextPayload(
string: "Hello NFC",
locale: Locale(identifier: "en")
)
// 自訂承載
let customPayload = NFCNDEFPayload(
format: .nfcExternal,
type: "com.example:mytype".data(using: .utf8)!,
identifier: Data(),
payload: "custom-data".data(using: .utf8)!
)
解析承載內容
請載入 Parsing NDEF Payload Content 以取得完整的類型名稱格式切換及多記錄處理。
背景標籤讀取
在 iPhone XS 及後續機型上,iOS 可以在背景讀取 NFC 標籤,無需開啟你的 App。NDEF 訊息必須包含一個 URI 記錄(typeNameFormat == .nfcWellKnown,類型 U)。如果有多個 URI 記錄,系統會使用第一個。
若要進行 App 特定的路由,請將通用連結寫入標籤,並為該網域設定 Associated Domains 功能。背景標籤讀取也支援特定的系統 URL 方案,例如網頁、電子郵件、簡訊、電話、FaceTime、地圖和 HomeKit 設定。它不支援自訂 URL 方案,且系統不會根據 bundle ID 或任意 NDEF 內容類型進行路由。
當使用者點擊相容的標籤時,iOS 會顯示一個通知,開啟你的 App。透過 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 硬體。請使用目前的 TAG 值作為 Core NFC 讀取器會話;不要複製加入 NDEF 的舊範例。
不要:忽略會話失效錯誤
會話會因多種原因失效。區分使用者取消與實際錯誤,可避免顯示錯誤的警告。
// 錯誤——使用者取消時顯示錯誤
func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
showAlert("NFC 錯誤:\(error.localizedDescription)")
}
// 正確——過濾預期的失效原因
func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
let nfcError = error as? NFCReaderError
switch nfcError?.code {
case .readerSessionInvalidationErrorUserCanceled,
.readerSessionInvalidationErrorFirstNDEFTagRead:
break // 正常終止
default:
showAlert("NFC 錯誤:\(error.localizedDescription)")
}
self.session = nil
}
不要:持有已失效會話的強引用
會話一旦失效,就無法重新啟動。將引用設為 nil,並為下一次掃描建立新的會話。
// 錯誤——重複使用已失效的會話
func scanAgain() {
session?.begin() // 無效,會話已失效
}
// 正確——建立新的會話
func scanAgain() {
session = NFCNDEFReaderSession(
delegate: self, queue: nil, invalidateAfterFirstRead: false
)
session?.begin()
}
審查清單
- [ ] 在 Signing & Capabilities 中新增 NFC 功能
- [ ] 在 Info.plist 中設定
NFCReaderUsageDescription - [ ]
com.apple.developer.nfc.readersession.formats授權使用TAG,而非舊版的NDEF - [ ] 在建立會話前檢查
NFCNDEFReaderSession.readingAvailable或NFCTagReaderSession.readingAvailable - [ ] 在呼叫
begin()之前設定會話委派 - [ ] 會話失效後將會話引用設為 nil
- [ ]
didInvalidateWithError區分使用者取消與實際錯誤 - [ ] 在寫入操作前查詢 NDEF 狀態
- [ ] 在寫入大型訊息前檢查標籤容量
- [ ] 若使用
NFCTagReaderSession,在 Info.plist 中列出 ISO 7816 應用程式識別碼 - [ ] 輪詢
.iso18092時,在 Info.plist 中列出 FeliCa 系統代碼 - [ ] 背景標籤讀取使用 URI NDEF 記錄以及通用連結或支援的系統 URL 方案
- [ ] 背景路由不使用自訂 URL 方案、bundle ID 或任意 NDEF 內容類型
- [ ] 與支付相關的 AID 不會路由到
NFCTagReaderSession - [ ] 一次只有一個讀取器會話處於活動狀態
參考資料
- 擴展模式(ISO 7816 指令、多標籤掃描、NDEF 鎖定):references/nfc-patterns.md
- Core NFC 框架
- NFCNDEFReaderSession
- NFCTagReaderSession
- NFCNDEFMessage
- NFCNDEFPayload
- NFCNDEFTag
- NFCNDEFReaderSessionDelegate
- NFCTagReaderSessionDelegate
- Building an NFC Tag-Reader App
- Adding Support for Background Tag Reading
- Near Field Communication Tag Reader Session Formats Entitlement
- ISO7816 application identifiers for NFC Tag Reader Session
- ISO18092 system codes for NFC Tag Reader Session






