class NFCReader: NSObject, ObservableObject, NFCTagReaderSessionDelegate { var nfcSession: NFCTagReaderSession? let a_AID = "A00001" let b_AID = "B00002" let verify = "00200000000000" let readBinary = "00B00000000000" let getDataRequest = "00CA000000" func startSession() { nfcSession = NFCTagReaderSession(pollingOption: .iso14443, delegate: self) nfcSession?.alertMessage = "Hold Your Phone Near the NFC Tag" nfcSession?.begin() } func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) { print(#function) } func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) { print(#function) if let readerError = error as? NFCReaderError { print("code:", readerError.code) if (readerError.code != .readerSessionInvalidationErrorFirstNDEFTagRead) && (readerError.code != .readerSessionInvalidationErrorUserCanceled) { DispatchQueue.main.async { session.alertMessage = "Session Invalidated: " + error.localizedDescription print("Session Invalidated", error.localizedDescription) } } } nfcSession = nil } func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) { print(#function, "tag.count:", tags.count)// Only one can be obtained each time if tags.count > 1 { let retryInterval = DispatchTimeInterval.milliseconds(500) session.alertMessage = "More than 1 tag is detected, please remove all tags and try again." DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: { session.restartPolling() }) return } let tag = tags.first! session.connect(to: tag) { (error) in if nil != error { session.invalidate(errorMessage: "Could not connect. Please read again.") return } guard case .iso7816(let typeBTag) = tag else { let retryInterval = DispatchTimeInterval.milliseconds(500) session.alertMessage = "It does not correspond" DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval, execute: { session.restartPolling() }) return } let AID = typeBTag.initialSelectedAID print("AID: \(AID)") switch AID { case self.a_AID: let sendData = self.verify.hexData let myAPDU = NFCISO7816APDU.init(data: sendData)! typeBTag.sendCommand(apdu: myAPDU) { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in debugPrint(response) if let error = error { debugPrint(error) print("Application failure / Verify") session.invalidate(errorMessage: "Application failure / Verify") return } if !(sw1 == 0x90 && sw2 == 0) { print("Application failure / Verify") session.invalidate(errorMessage: "Application failure / Verify : sw1=\(sw1), sw2=\(sw2)") return } // READ BINARY let sendDataReadBinary = self.readBinary.hexData let myAPDU2 = NFCISO7816APDU.init(data: sendDataReadBinary)! typeBTag.sendCommand(apdu: myAPDU2) { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in debugPrint(response) print("sw1=\(sw1)", "sw2=\(sw2)") if let error = error { debugPrint(error) print("Application failure / READ BINARY") session.invalidate(errorMessage: "Application failure / READ BINARY") return } if !(sw1 == 0x90 && sw2 == 0) { print("Application failure / READ BINARY") session.invalidate(errorMessage: "Application failure / READ BINARY : sw1=\(sw1), sw2=\(sw2)") return } print("a_AID:", String(data: response, encoding: .ascii)!) session.alertMessage = "sucess" // without calling invalidate(), I want to connect b_AID session.invalidate() } } // I failed to get the tag. But I want to read b_AID after reading a_AID in a row case self.b_AID: let sendData = self.getDataRequest.hexData let printerAPDU = NFCISO7816APDU(data: sendData)! typeBTag.sendCommand(apdu: printerAPDU) { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in debugPrint(response) print("sw1=\(sw1)", "sw2=\(sw2)") if let error = error { debugPrint(error) print("Application failure / b_AID") session.invalidate(errorMessage: "Application failure / b_AID") return } if !(sw1 == 0x90 && sw2 == 0) { print("Application failure / b_AID") session.invalidate(errorMessage: "Application failure / b_AID : sw1=\(sw1), sw2=\(sw2)") return } print("b_AID:", String(data: response, encoding: .ascii)!) session.alertMessage = "sucess" session.invalidate() } default: session.alertMessage = "Not supported" session.invalidate() } } } } fileprivate func convertHex(_ s: String.UnicodeScalarView, i: String.UnicodeScalarIndex, appendTo d: [UInt8]) -> [UInt8] { let skipChars = CharacterSet.whitespacesAndNewlines guard i != s.endIndex else { return d } let next1 = s.index(after: i) if skipChars.contains(s[i]) { return convertHex(s, i: next1, appendTo: d) } else { guard next1 != s.endIndex else { return d } let next2 = s.index(after: next1) let sub = String(s[i..<next2]) guard let v = UInt8(sub, radix: 16) else { return d } return convertHex(s, i: next2, appendTo: d + [ v ]) } } extension String { var hex : [UInt8] { return convertHex(self.unicodeScalars, i: self.unicodeScalars.startIndex, appendTo: []) } var hexData : Data { return Data(convertHex(self.unicodeScalars, i: self.unicodeScalars.startIndex, appendTo: [])) } } extension Data { var hexString : String { return self.reduce("") { (a : String, v : UInt8) -> String in return a + String(format: "%02x", v) } } }