You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Swift中使用CoreNFC扫描认证Ntag213加密NFC标签失败

问题分析与修复方案

核心问题点

  1. 回调优先级冲突:实现readerSession(_:didDetect:)后,系统会停止自动触发readerSession(_:didDetectNDEFs:);且密码保护的Ntag213未认证前无法读取NDEF,原自动读取逻辑完全失效。
  2. 认证数据格式错误:Ntag213要求PWD_AUTH命令的密码为4字节、PACK响应为2字节,需确保Base64解码后的数据长度符合要求。
  3. 缺少认证后读取逻辑:仅完成认证操作,未执行NDEF内容读取,无法获取标签数据。
  4. 错误提示未生效: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:07:04