CoreNFC NFCISO7816APDU sendCommand Tag connection lost

So i am tring to use the new CoreNFC features to access my transport card ( iso14443 tag type iso7816)

I have been able to detect the card send multiple apdu's to the card and get the card number and the purse balance using NFCISO7816APDU class and the tag.sendComand method.

However i have moved on to initiating a reload of the cards purse with a init realod apdu how ever this comman fails most of the time(it has worked sparatically) with a Tag connection lost error


SnapperCardReader[2543:876854] [CoreNFC] 00000002 83ac9580 -[NFCTagReaderSession transceive:tagUpdate:error:]:706 Error Domain=NFCError Code=100 "Tag connection lost" UserInfo={NSLocalizedDescription=Tag connection lost}


I have been able to run other apdu's after this failure and they have worked. this has leads me to belive there might be a spesific timeout in


func sendCommand(apdu: NFCISO7816APDU, completionHandler: @escaping (Data, UInt8, UInt8, Error?) -> Void)


and my card is responding too slowly. does anyone have any expireance with this or could provide some help as to what to do next?

Replies

here is the code i have been using to send the apdu's


func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {

print("got a Tag!")

print("\(tags)")

let nfcTag = tags.first!

print("connecting to Tag!")

nfcTagReaderSession?.connect(to: nfcTag) { (error1: Error?) in

if error1 != nil{

print(error1!)

}

if case let .iso7816(sTag) = nfcTag {

print("sellecting application on card!")

let selectApp = NFCISO7816APDU.init(data:Data.init([0,0xa4,0x04, 0x00, 0x7, 0xd4, 0x10, 0x00, 0x00, 0x03, 0x00, 0x01]))

sTag.sendCommand(apdu: selectApp!) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in

if error != nil{

print(error!)

return

}else if data.count > 2{

if data[7] != 0{

print("select app worked")

self.app = data

let cardNumber = (BinaryTools.getDataSection(data: self.app!, offset: 8, length: 8)).hexEncodedString()

DispatchQueue.main.async {

self.cardNum.text = "Card Number:\(cardNumber)"

}

}

}

}

print("reading purse")

let readPurse = NFCISO7816APDU.init(data:self.getPurseAPDU(purseNumber: 1))

sTag.sendCommand(apdu: readPurse!) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in

if error != nil{

print(error!)

return

}

print("reading purse worked")

self.purseData = data

let balance = BinaryTools.getDataSection(data: self.purseData!, offset: 2, length: 4).getIntFromData()

DispatchQueue.main.async {

self.balance.text = "Balance:\(String(format: "$%.02f",(Double(balance))/100))"

}

}


let amountBytes = BinaryTools().getBigEndianBytes(number: UInt32(100))

let apdu = Data.init([0x90,0x40,0x00,0x00,0x04,amountBytes[0],amountBytes[1],amountBytes[2],amountBytes[3]])

let initLoad = NFCISO7816APDU.init(data:apdu)

print("init load")

sTag.sendCommand(apdu: initLoad!) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in

if error != nil{

print("load error \(String(describing: error))")

return

}else {

print("init load worked")

}

}

}

}

}

I also have this problem.

When I want to readWithoutEncryption, it also show tag connection lost.

...

need help

Can you share your project code? When I sendCommand first time, It shows "


Error Domain=NFCError Code=2 "Missing required entitlement" UserInfo={NSLocalizedDescription=Missing required entitlement}


"

I need help! maybe my configure incorrect

I belive you need to add the AID for your spesific card to your info.plist for example mine is.


<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>

<array>

<string>D4100000030001</string>

</array>


also check you entitlments file has TAG in it

<key>com.apple.developer.nfc.readersession.formats</key>

<array>

<string>NDEF</string>

<string>TAG</string>

</array>

Thanks for your reply, It works after I replace the correct AID🙂

@CrocoPig


How to get data dynamically?


NFCISO7816APDU.init(data:Data.init([0,0xa4,0x04, 0x00, 0x7, 0xd4, 0x10, 0x00, 0x00, 0x03, 0x00, 0x01])) <- this.


I tried using Mac algo. Can you guide?

Can you add the code for the missing finctions:


BinaryTools

getPurseAPDU


Thanks

we used the same code but the session getting timeout after 15 sec while connecting and we are not able to send apdu command


what acatually happens is first we need to connect later we need to send command, but here when we call connect code it also calling send command at a time asynchronusly after 15 sec we are facing "session timeout " error issue.


some times after "session timeout" we also getting "Tag not connected" error.


please provide full code or links for successful nfc authentication

I am able to connect to the card using NFCTagReaderSession and NFCISO7816Tag tag but i can't get ATR number after connection and it shows zero bytes in historicalBytes, while in android we are able to get some number in historicalBytes for same type of cards\


One more filed we have NFCISO7816Tag "identifier" but it is getting different number for same type of card.


print("iso7816Tag historical bytes \(iso7816Tag.historicalBytes)")

print("iso7816Tag identifier \(iso7816Tag.identifier)")


We want same number or value for same type of card that can be used for ATR validation in our code.


Please suggest to get an ATR number or value for same type of cards.

I've also been working with the NFCTagReaderSession, NFCISO7816Tag, and NFCISO7816APDU Core-NFC elements to communicate with NFC-Devices via the ISO14443/ISO7816 protocals.


Right now I'm trying to reduce the Communication Failure Rate which I have found is directly proportional to three factors;

1) The Size of the APDU Command sent by IOS, and the Size of the APDU Response of the NFC-Device.

Where the larger they are, the more likely communication is to fail.


2) The number of APDU Command/Response pairs you are sending/processing in a row via a single Session.

The more APDU commands you try to send during a Session, the more likely communication is to fail.


3) The positioning of the IOS-Device relative to the NFC-Device when initiating the Scan.

The closer you are to the RF-Field of the NFC-Device when you begin a Scan, the more likely communication is to fail.

The less Direct the Angle of approach of the IOS-Device to the RF-Field of the NFC-Device, the more likely communication is to fail.


Point 3 is the largest contributing factor, where communication can fail 90% of the time.

Point 2 is the next largest contributor, where communication starts failing 40-50% of the time just from trying to Send/Process Two

APDU Command/Response pairs during a Session.


For reference, this Forum Post also discusses Communication Failure that has been observed

~ see https://forums.developer.apple.com/message/393076#393076



When communication explicitly fails, I get one of two Errors and Debbugger-Console-Messages

within the completionHandler of the NFCISO7816Tag.sendCommand method;

1)

Debugger-Log:

2019-11-18 15:20:35.143756-0500 NFC-RW-Basic[1413:723511] [CoreNFC] 00000002 80c02600

-[NFCTagReaderSession _connectTag:error:]:573

Error Domain=NFCError Code=100 "Stack Error"

UserInfo={

NSLocalizedDescription=Stack Error,

NSUnderlyingError=0x282c90990 {

Error Domain=nfcd Code=29 "Tag Error"

UserInfo={NSLocalizedDescription=Tag Error}

}

}


Error:

Domain=NFCError Code=104 "Tag is not connected" UserInfo={NSLocalizedDescription=Tag is not connected}



2)

Debugger-Log:

2019-11-18 15:40:25.913306-0500 NFC-RW-Basic[1418:727395] [CoreNFC] 00000002 82031300

-[NFCTagReaderSession transceive:tagUpdate:error:]:735

Error Domain=NFCError Code=100 "Tag connection lost"

UserInfo={

NSLocalizedDescription=Tag connection lost

}


Error:

Domain=NFCError Code=100 "Tag connection lost" UserInfo={NSLocalizedDescription=Tag connection lost}



TheNFCError Code=104 "Tag is not connected"error being the more common of the two that occurs.

But I've also seen behavior where upon initiating the Scan, the IOS-Device does not detect the NFC-Device at all

and eventually times out (after 60 seconds by default) if you do not Cancel.



I've found that using the NFCTagReaderSession.restartPolling() method within the

completionHandler of NFCISO7816Tag.sendCommand can help mask the communication failures from the user,

where eventually communication may succeed at some point during the Session.


nfciso7816Tag.sendCommand(apdu: readAPDU) { (responseA: Data, sw1A: UInt8, sw2A: UInt8, errorA: Error?) in


    guard error == nil && (sw1 == 0x90 && sw2 == 0x00) else {


        let retryInterval = DispatchTimeInterval.milliseconds(100)
        DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: {
            session.restartPolling()
        })
        return
        
    }

This does not help however for the case when the IOS-Device has started a Scan that fails to detect the NFC-Device.


There is also an NFCTagCommandConfiguration class

that seems like it could potentially help in configuring the NFCTagReaderSession to better reduce/handle Communication failure,

but I've yet to see any documentation on how to actually use it

~ see https://forums.developer.apple.com/thread/125199

I am working on scanning a MiFare tag and reading the EEPROM (NDEF) and SRAM part by sending mifareTag.sendMiFareCommands. This works very well in a test app where I just feed static commands from an array to the tag, but gives a lot of problems (Tag connection lost or responseBlocks not giving back results or ACK's), when giving exactly the same commands from an app where all command parameters are calculated. What (probably...) solved it for me was defining mij own serial queue:


let tagScanQueue = DispatchQueue(label: "xxxxxxxx.tagScanQueue", qos: .userInitiated)


tagReaderSession = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: tagScanQueue)


and wrapping every function calling my mifareTag.sendMiFareCommands function in tagScanQueue.asyncAfter:


    private func sendCommandXxxxxx(to mifareTag: NFCMiFareTag) {
        tagScanQueue.async {
            self.sendCommand(xxxxxx, to: mifareTag) {
                result in
                    ...
           }
        }
    }


    private func sendCommand(_ mfCommand: MFCommandContent, to mifareTag: NFCMiFareTag,
                             returnResult: @escaping (Data) -> Void) {
        tagScanQueue.asyncAfter(deadline: .now() + .milliseconds(mfCommand.delay)) {
            mifareTag.sendMiFareCommand(commandPacket: mfCommand.command) {
                responseBlock, error in
                if let error = error {
                    self.invalidateScan(.tagInvalid(messageCode: 123)) // MiFare command gives an error
                    return
                }
                guard responseBlock.count != 1 ||
                    responseBlock == Data([0x0A]) else {
                        self.invalidateScan(.tagInvalid(messageCode: 456)) // MiFare command gives an invalid response
                        return
                }
                returnResult(responseBlock)
            }
        }
    }

Maybe this helps you?

Sorry for not updating the post the issue was to do with a bad repair job on the only test device. once i got some more devices to test with all the rest of iphones worked perfectly. after some investigation i found out the previous owner had the iphone repired after some screen and apple pay issues.

@CrocoPig :

I'm cunfused regarding the BinaryTools.getDataSection and , can you explain more bout that ??
Did anyone resolve this? Seeing the same issue for iPhone models prior to iPhone 11 only (iOS versions are all the latest for iphone 8 and X we ran tests on - it works 30-40% of the time or connection to tag is lost, tag is detected multiple times without moving it, or the select AID process is done repeatedly with success and doesn't execute the APDU command.) It works 100% of the time with iphone 11.