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

SwiftUI中使用Core NFC遇代理回调异常问题求助

解决SwiftUI中NFCNDEFReaderSessionDelegate回调不触发的问题

看起来你遇到的核心问题有两个:代理对象生命周期过短,以及CoreNFC版本兼容的方法实现问题,下面一步步给你拆解解决:

问题根源分析

  1. 实例生命周期问题:你在scan()方法里创建的Nfc是局部变量,方法执行完就被ARC回收了。当NFC会话试图触发回调时,代理对象已经不存在,系统自然找不到对应的实现方法,这就是你看到Delegate does not implement -readerSessionDidBecomeActive:错误的原因。
  2. CoreNFC版本差异:iOS 13之后,didDetectNDEFs方法被弃用,官方推荐使用didDetect tags方法,但这个新方法需要手动完成标签连接、NDEF状态查询、消息读取的完整流程,不是简单打印就能工作的。

修改后的完整代码

1. 重构Nfc类(实现ObservableObject+完整NFC流程)

import CoreNFC
import SwiftUI

class Nfc: NSObject, NFCNDEFReaderSessionDelegate, ObservableObject {
    // 用@Published把扫描结果传递给ContentView
    @Published var scanResult: String = "n/a"
    
    func start() {
        // 先检查设备是否支持NFC读取
        guard NFCNDEFReaderSession.readingAvailable else {
            scanResult = "当前设备不支持NFC读取"
            return
        }
        let session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
        session.begin()
        print("NFC扫描会话已启动")
    }
    
    // iOS 11-12兼容方法(iOS13+已弃用)
    func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        processReceivedMessages(messages)
        session.invalidate()
    }
    
    // iOS13+必须实现的新方法:处理标签检测
    func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
        guard let tag = tags.first else {
            session.invalidate(errorMessage: "未检测到NFC标签")
            return
        }
        
        // 连接到标签
        session.connect(to: tag) { error in
            if let error = error {
                session.invalidate(errorMessage: "连接标签失败:\(error.localizedDescription)")
                return
            }
            
            // 查询标签的NDEF支持状态
            tag.queryNDEFStatus { status, _, error in
                if let error = error {
                    session.invalidate(errorMessage: "查询NDEF状态失败:\(error.localizedDescription)")
                    return
                }
                
                switch status {
                case .notSupported:
                    session.invalidate(errorMessage: "该标签不支持NDEF格式")
                case .readOnly, .readWrite:
                    // 读取标签中的NDEF消息
                    tag.readNDEF { message, error in
                        if let error = error {
                            session.invalidate(errorMessage: "读取NDEF消息失败:\(error.localizedDescription)")
                            return
                        }
                        
                        if let message = message {
                            self.processReceivedMessages([message])
                        } else {
                            self.scanResult = "标签中无NDEF消息"
                        }
                        session.invalidate()
                    }
                @unknown default:
                    session.invalidate(errorMessage: "未知的NDEF状态")
                }
            }
        }
    }
    
    func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
        print("NFC会话已激活")
    }
    
    func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        if let readerError = error as? NFCReaderError {
            // 排除用户主动取消的情况
            if readerError.code != .readerSessionInvalidationErrorUserCanceled {
                scanResult = "扫描错误:\(readerError.localizedDescription)"
            }
        } else {
            scanResult = "意外错误:\(error.localizedDescription)"
        }
        print("NFC会话已终止:\(error.localizedDescription)")
    }
    
    // 统一处理读取到的NDEF消息
    private func processReceivedMessages(_ messages: [NFCNDEFMessage]) {
        let content = messages.flatMap { $0.records }
            .compactMap { String(data: $0.payload, encoding: .utf8) }
            .joined(separator: "\n")
        scanResult = content.isEmpty ? "未读取到有效内容" : content
    }
}

2. 修改ContentView(持有Nfc实例)

struct ContentView: View {
    // 用@StateObject让Nfc实例在View生命周期内保持存活
    @StateObject private var nfc = Nfc()
    
    var body: some View {
        VStack(spacing: 20) {
            Text(nfc.scanResult)
                .padding()
                .multilineTextAlignment(.center)
            
            Button(action: {
                nfc.start()
            }) {
                Text("扫描NFC标签")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(8)
            }
            // 当设备不支持NFC时禁用按钮
            .disabled(!NFCNDEFReaderSession.readingAvailable)
        }
        .padding()
    }
}

额外配置提醒

  1. 在Info.plist中添加NFC权限描述:
    <key>NFCReaderUsageDescription</key>
    <string>需要使用NFC读取标签内容</string>
    
  2. 必须使用真机测试,iOS模拟器不支持NFC功能。
  3. 确保测试设备是iPhone 7及以上(支持NFC读取),且已开启系统NFC功能。

内容的提问来源于stack exchange,提问作者CL So

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:06:22