iOS Swift中使用CoreNFC扫描认证Ntag213加密NFC标签失败
问题分析与修复方案
核心问题点
- 回调优先级冲突:实现
readerSession(_:didDetect:)后,系统会停止自动触发readerSession(_:didDetectNDEFs:);且密码保护的Ntag213未认证前无法读取NDEF,原自动读取逻辑完全失效。 - 认证数据格式错误:Ntag213要求
PWD_AUTH命令的密码为4字节、PACK响应为2字节,需确保Base64解码后的数据长度符合要求。 - 缺少认证后读取逻辑:仅完成认证操作,未执行NDEF内容读取,无法获取标签数据。
- 错误提示未生效:
didInvalidateWithError中弹窗逻辑未在主线程正确执行,导致错误信息无法展示。
修正后的代码
import UIKit import CoreNFC class MessagesTableViewController: UITableViewController, NFCNDEFReaderSessionDelegate { let reuseIdentifier = "reuseIdentifier" var detectedMessages = [NFCNDEFMessage]() var session: NFCNDEFReaderSession? @IBAction func beginScanning(_ sender: Any) { guard NFCNDEFReaderSession.readingAvailable else { let alertController = UIAlertController( title: "扫描不支持", message: "此设备不支持标签扫描。", preferredStyle: .alert ) alertController.addAction(UIAlertAction(title: "确定", style: .default)) present(alertController, animated: true) return } session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false) session?.alertMessage = "将iPhone靠近标签以读取内容。" session?.begin() } // MARK: - NFCNDEFReaderSessionDelegate func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) { guard let tag = tags.first else { session.invalidate(errorMessage: "未检测到有效标签") return } session.connect(to: tag) { [weak self] error in guard let self = self else { return } if let error = error { print("连接标签失败:\(error.localizedDescription)") session.invalidate(errorMessage: "连接标签失败") return } guard let miFareTag = tag as? NFCMiFareTag else { session.invalidate(errorMessage: "不支持的标签类型") return } // 校验密码和PACK的字节长度 guard let pwdBytes = Data(base64Encoded: "xyzzzz"), pwdBytes.count == 4 else { print("密码格式错误,必须为4字节") session.invalidate(errorMessage: "密码配置错误") return } guard let packBytes = Data(base64Encoded: "ABC"), packBytes.count == 2 else { print("PACK格式错误,必须为2字节") session.invalidate(errorMessage: "PACK配置错误") return } // 发送PWD_AUTH认证命令 let authCommand = Data([0x1B]) + pwdBytes miFareTag.sendMiFareCommand(commandPacket: authCommand) { [weak self] response, error in guard let self = self else { return } if let error = error { print("认证失败:\(error.localizedDescription)") session.invalidate(errorMessage: "标签认证失败") return } // 验证PACK响应匹配 guard response == packBytes else { print("PACK不匹配,认证失败") session.invalidate(errorMessage: "认证信息不匹配") return } print("标签认证成功") session.alertMessage = "认证成功,正在读取内容..." // 认证成功后读取NDEF数据 tag.queryNDEFStatus { status, capacity, error in if let error = error { print("查询NDEF状态失败:\(error.localizedDescription)") session.invalidate(errorMessage: "读取标签内容失败") return } guard status == .readWrite || status == .readOnly else { session.invalidate(errorMessage: "标签无NDEF内容") return } tag.readNDEF { message, error in if let error = error { print("读取NDEF失败:\(error.localizedDescription)") session.invalidate(errorMessage: "读取标签内容失败") return } guard let ndefMessage = message else { session.invalidate(errorMessage: "标签无NDEF内容") return } DispatchQueue.main.async { self.detectedMessages.append(ndefMessage) self.tableView.reloadData() session.alertMessage = "读取成功" // 若无需继续扫描,可调用session.invalidate()结束会话 } } } } } } func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) { guard let readerError = error as? NFCReaderError else { print("会话错误:\(error.localizedDescription)") return } if readerError.code != .readerSessionInvalidationErrorFirstNDEFTagRead && readerError.code != .readerSessionInvalidationErrorUserCanceled { let alertController = UIAlertController( title: "会话已失效", message: error.localizedDescription, preferredStyle: .alert ) alertController.addAction(UIAlertAction(title: "确定", style: .default)) DispatchQueue.main.async { self.present(alertController, animated: true) } } } // 原自动读取NDEF的回调不再生效,保留空实现避免编译错误 func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {} func addMessage(fromUserActivity ndefMessage: NFCNDEFMessage) { detectedMessages.append(ndefMessage) tableView.reloadData() } }
关键修正说明
- 回调逻辑重构:完全通过
didDetect tags手动处理连接、认证、读取全流程,废弃自动读取NDEF的无效逻辑。 - 数据格式校验:强制校验密码(4字节)和PACK(2字节)的长度,避免不符合标签要求的命令导致失败。
- 认证后读取流程:认证成功后,先查询NDEF状态再读取内容,确保能获取标签数据并更新UI。
- 错误提示修复:修正主线程弹窗逻辑,在各操作节点添加明确的错误日志和用户提示。
- 内存安全优化:使用
[weak self]避免循环引用,防止内存泄漏。
内容的提问来源于stack exchange,提问作者Varun Bantwal Shenoy
相关产品推荐
相关产品推荐

