Search code examples
iosswiftxcodenfcapdu

How to send multiple NFC ISO7816 APDUs in Swift (iOS)?


I need to send multiple (lets say 50) adpu messages to my iso 7816 nfc tag.

The order needs to be: sending -> receiving response -> do some calculations -> sending -> receiving response -> do some calculations -> sending -> ...

How can I do it?

This code prints "2" before "1" so there is no place to implement response1-dependent calculations:

    func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
        session.connect(to: tag) { (error1: Error?) in
        if nil != error1{
         session.invalidate(errorMessage: "Connection Failed")
        }
        if case let .iso7816(sTag) = tag{
            sTag.sendCommand(apdu: apdu1) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in
                if error != nil{
                    return
                }
                print("1")
            }
            print("2")
            sTag.sendCommand(apdu: apdu2) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in
                if error != nil{
                    return
                }
                // ...

This code works, but creates blocks in blocks. So there can probably happen stack oveflow and blocks in blocks are probably not ok with coding conventions etc:

    func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
        session.connect(to: tag) { (error1: Error?) in
        if nil != error1{
         session.invalidate(errorMessage: "Connection Failed")
        }
        if case let .iso7816(sTag) = tag{
            sTag.sendCommand(apdu: apdu1) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in
                if error != nil{
                    return
                }
                //create apdu2 depend on response1
                sTag.sendCommand(apdu: apdu2) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in
                    if error != nil{
                        print(error!)
                        return
                    }
                    //create apdu3 depend on response1 and response2
                    sTag.sendCommand(apdu: apdu3) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in
                        if error != nil{
                            print(error!)
                            return
                        }
                        // ...
        

Are there better alternatives or is this the only functionable solution?


Solution

  • I made it with semaphores, but there are more possibilities as @Paulw11 mentioned in comments

    func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
        session.connect(to: tag) { (error1: Error?) in
        if nil != error1{
         session.invalidate(errorMessage: "Connection Failed")
        }
        if case let .iso7816(sTag) = tag{
            DispatchQueue.global(qos: .background).async { //because semaphore.wait() shouldn't block main thread
                let semaphore = DispatchSemaphore(value: 0)
                let apdu1 = //...
                sendApdu(apdu: apdu1, semaphore: semaphore)
                semaphore.wait()
                sendApdu(apdu: apdu2, semaphore: semaphore)
                semaphore.wait()
                sendApdu(apdu: apdu3, semaphore: semaphore)
                semaphore.wait()
            }
        }
    }
    
    func sendApdu(apdu: NFCISO7816APDU, semaphore: DispatchSemaphore){
        sTag.sendCommand(apdu: apdu3) { (data:Data, int1:UInt8, int2:UInt8, error:Error?) in
            if error != nil{
                print(error!)
                return
            }
            //... analyse response, put next apdu to a class variable etc
            semaphore.signal()
        }
    }