NFCTagReaderSession host card emulation (HCE) exception

I'm trying to read a tag generated by my app that emulates event input tags; in my NFC reader app and I get this error:

<NSXPCConnection: 0x300a108c0> connection to service with pid 62 named com.apple.nfcd.service.corenfc: Exception caught during decoding of received selector didDetectExternalReaderWithNotification:, dropping incoming message. Exception: Exception while decoding argument 0 (#2 of invocation): Exception: decodeObjectForKey: class "NFFieldNotificationECP1_0" not loaded or does not exist ( 0 CoreFoundation 0x0000000192b00f2c 76A3B198-3C09-323E-8359-0D4978E156F5 + 540460 1 libobjc.A.dylib 0x000000018a9a32b8 objc_exception_throw + 60 2 Foundation 0x0000000191932584 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 38276 3 Foundation 0x0000000191930d10 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 32016 4 Foundation 0x000000019198f460 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 418912 5 Foundation 0x000000019198c510 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 406800 6 Foundation 0x00000001919e4cf4 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 769268 7 Foundation 0x00000001919e3a60 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 764512 8 Foundation 0x00000001919e31c4 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 762308 9 Foundation 0x00000001919e307c D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 761980 10 libxpc.dylib 0x00000001ef5a1cbc CD0F76A8-713A-3FDB-877E-386B089BC2D1 + 72892 11 libxpc.dylib 0x00000001ef5a3908 CD0F76A8-713A-3FDB-877E-386B089BC2D1 + 80136 12 libdispatch.dylib 0x000000010401e87c _dispatch_client_callout4 + 20 13 libdispatch.dylib 0x000000010403bec4 _dispatch_mach_msg_invoke + 516 14 libdispatch.dylib 0x00000001040264a4 _dispatch_lane_serial_drain + 376 15 libdispatch.dylib 0x000000010403cea8 _dispatch_mach_invoke + 480 16 libdispatch.dylib 0x00000001040264a4 _dispatch_lane_serial_drain + 376 17 libdispatch.dylib 0x000000010402743c _dispatch_lane_invoke + 460 18 libdispatch.dylib 0x0000000104034404 _dispatch_root_queue_drain_deferred_wlh + 328 19 libdispatch.dylib 0x0000000104033a38 _dispatch_workloop_worker_thread + 444 20 libsystem_pthread.dylib 0x00000001ef550934 _pthread_wqthread + 288 21 libsystem_pthread.dylib 0x00000001ef54d0cc start_wqthread + 8 ) ( 0 CoreFoundation 0x0000000192b00f2c 76A3B198-3C09-323E-8359-0D4978E156F5 + 540460 1 libobjc.A.dylib 0x000000018a9a32b8 objc_exception_throw + 60 2 Foundation 0x000000019198f680 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 419456 3 Foundation 0x000000019198c510 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 406800 4 Foundation 0x00000001919e4cf4 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 769268 5 Foundation 0x00000001919e3a60 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 764512 6 Foundation 0x00000001919e31c4 D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 762308 7 Foundation 0x00000001919e307c D27A6EC5-943C-3B0E-8D15-8840FD2914F0 + 761980 8 libxpc.dylib 0x00000001ef5a1cbc CD0F76A8-713A-3FDB-877E-386B089BC2D1 + 72892 9 libxpc.dylib 0x00000001ef5a3908 CD0F76A8-713A-3FDB-877E-386B089BC2D1 + 80136 10 libdispatch.dylib 0x000000010401e87c _dispatch_client_callout4 + 20 11 libdispatch.dylib 0x000000010403bec4 _dispatch_mach_msg_invoke + 516 12 libdispatch.dylib 0x00000001040264a4 _dispatch_lane_serial_drain + 376 13 libdispatch.dylib 0x000000010403cea8 _dispatch_mach_invoke + 480 14 libdispatch.dylib 0x00000001040264a4 _dispatch_lane_serial_drain + 376 15 libdispatch.dylib 0x000000010402743c _dispatch_lane_invoke + 460 16 libdispatch.dylib 0x0000000104034404 _dispatch_root_queue_drain_deferred_wlh + 328 17 libdispatch.dylib 0x0000000104033a38 _dispatch_workloop_worker_thread + 444 18 libsystem_pthread.dylib 0x00000001ef550934 _pthread_wqthread + 288 19 libsystem_pthread.dylib 0x00000001ef54d0cc start_wqthread + 8 )

This is my code:

HCE app:

@available(iOS 17.4, *)
func cardSession(accessCode: String) {
    print("Access code: \(accessCode)")
    let ProcessAPDU: (_ capdu: Data) -> Data = { capdu in
        guard let responseAPDU = accessCode.data(using: .utf8) else {
            print("Failed to convert access code to Data")
            return Data()
        }
        return responseAPDU
    }
    
    Task {
        guard NFCReaderSession.readingAvailable,
              CardSession.isSupported,
              await CardSession.isEligible else {
            self.showQRCode()
            return
        }

        self.cancelSession()
        
        do {
            (UIApplication.shared.delegate as? AppDelegate)?.currentCardSession = try await CardSession()
        } catch {
            self.showQRCode()
            return
        }
        
        guard let cardSession = (UIApplication.shared.delegate as? AppDelegate)?.currentCardSession as? CardSession else {
            print("Card session is nil or not of type CardSession")
            self.showQRCode()
            return
        }
        
        for try await event in cardSession.eventStream {
            switch event {
            case .sessionStarted:
                print("Session started")
                cardSession.alertMessage = NSLocalizedString("Communicating with ticket reader.", comment: "")
                if await !cardSession.isEmulationInProgress {
                    try await cardSession.startEmulation()
                }
            case .readerDetected:
                print("Reader detected")
                if await !cardSession.isEmulationInProgress {
                    try await cardSession.startEmulation()
                }
            case .readerDeselected:
                print("Reader deselected")
            case .received(let cardAPDU):
                do {
                    let responseAPDU = ProcessAPDU(cardAPDU.payload)
                    try await cardAPDU.respond(response: responseAPDU)
                    await cardSession.stopEmulation(status: .success)
                    print("Received APDU: \(cardAPDU.payload)")

                } catch {
                    await cardSession.stopEmulation(status: .failure)
                    print("Error processing APDU: \(error.localizedDescription)")

                }
                self.cancelSession()
            case .sessionInvalidated(reason: let reason):
                print("Session invalidated: \(reason)")
                cardSession.alertMessage = NSLocalizedString("Ending communication with ticket reader.", comment: "")
            @unknown default:
                print("Unknown event: \(event)")
                break
            }
        }
    }
}

Reader app:

func startNFCSession() {
    nfcSession = NFCTagReaderSession(pollingOption: [.iso14443], delegate: self, queue: nil)
    nfcSession?.alertMessage = NSLocalizedString("Hold the device close to scan the ticket.", comment: "")
    nfcSession?.begin()
}

func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
    
    guard let tag = tags.first else {
        session.invalidate(errorMessage: NSLocalizedString("No tags found", comment: ""))
        return
    }
    
    guard case let .iso7816(iso7816Tag) = tag else {
        session.invalidate(errorMessage: NSLocalizedString("Tag is not ISO7816", comment: ""))
        return
    }
    
    Task { [iso7816Tag] in
        do {
            try await session.connect(to: tag)
            
            do {
                let apdu = NFCISO7816APDU(instructionClass: 0x00,
                                          instructionCode: 0xA4,
                                          p1Parameter: 0x04,
                                          p2Parameter: 0x00,
                                          data: Data([0xF0, 0x01, 0x02, 0x03, 0x04, 0x05]),
                                          expectedResponseLength: -1)

                var (data, sw1, sw2) = try await iso7816Tag.sendCommand(apdu: apdu)
                print("Response: \(data), SW1: \(sw1), SW2: \(sw2)")
                
                // Verificar que la respuesta sea válida
                if sw1 == 0x90 && sw2 == 0x00 {
                    if let accessCode = String(data: data, encoding: .utf8) {
                        print("Access Code: \(accessCode)")
                        self.verifyTicket(self.getSectors(), accessCode)
                        session.invalidate()
                    } else {
                        print("Failed to decode access code")
                        session.invalidate(errorMessage: NSLocalizedString("Failed to decode access code", comment: ""))
                    }
                } else if sw1 == 0x6A && sw2 == 0x82 {
                    session.invalidate(errorMessage: NSLocalizedString("File not found", comment: ""))
                } else if sw1 == 0x6A && sw2 == 0x86 {
                    session.invalidate(errorMessage: NSLocalizedString("Incorrect parameters P1-P2", comment: ""))
                } else {
                    session.invalidate(errorMessage: String(format: NSLocalizedString("Invalid response: %02x%02x", comment: ""), sw1, sw2))
                }
            } catch {
                print("Sending comment error: \(error.localizedDescription)")
                session.invalidate(errorMessage: String(format: NSLocalizedString("Error sending command: %@", comment: ""), error.localizedDescription))
            }
        } catch {
            if let nfcReaderError = error as? NFCReaderError {
                print(nfcReaderError.code)
                print(nfcReaderError.localizedDescription)
                session.invalidate(errorMessage: String(format: NSLocalizedString(nfcReaderError.localizedDescription, comment: "")))
            } else {
                print(error.localizedDescription)
                session.invalidate(errorMessage: String(format: NSLocalizedString("Connection error: %@", comment: ""), error.localizedDescription))
            }
        }
    }

}

We would like to see a bug report about this including some diagnostic logs to understand what exactly might be going on.

Don't forget to include as much information as you can about the cases you see this problem with. And if you are able to, create a debug level sysdiagnose after reproducing the issue.

To do so, please go to https://developer.apple.com/bug-reporting/profiles-and-logs/ and follow the instructions for Wallet for iOS to install a logging profile on your device. Then, once the logging profile is installed:

  • reproduce the problem, and include details about the conditions at the time
  • Once the problem is reproduced, follow the instructions at the above link to trigger a sysdiagnose and attach it to the bug report
  • make sure you include your app's Bundle ID, any crash reports you have, and any other information you find relevant.

If you have a small sample project that shows your use of HCE and Reader modes, that would be helpful as well.

To file a bug report, you can use the Feedback Assistant.

Once you open the bug report, please post the FB number here for my reference.

If you have any questions about filing a bug report, take a look at Bug Reporting: How and Why?


Argun Tekant /  DTS Engineer / Core Technologies

NFCTagReaderSession host card emulation (HCE) exception
 
 
Q