반응형

NFC NDEF tag 읽기 / 쓰기 및 feliCa, iso7816, iso15693, miFare tag 읽기 관련 클래스를 만들어 보았다.

//
//  HiNfcManager.swift
//  NFC
//
//  Created by netcanis on 2023/04/18.
//

import UIKit
import CoreNFC

public enum HiNFCError: Error {
    case unavailable
    case notSupported
    case readOnly
    case invalidPayloadSize
    case invalidated(errorDescription: String)
}

open class HiNFCManager: NSObject {
    static let shared = HiNFCManager()
    
    public typealias DidBecomeActive = (HiNFCManager) -> Void
    public typealias DidDetect = (HiNFCManager, Result<[String: Any]?, HiNFCError>) -> Void
    
    private enum HiNFCAction {
        case read
        case write(message: NFCNDEFMessage)
    }
    
    // MARK: - Properties
    private var didBecomeActive: DidBecomeActive?
    private var didDetect: DidDetect?
    private var action: HiNFCAction?
    
    // MARK: - Properties (NDEF, Tag)
    open private(set) var ndefSession: NFCNDEFReaderSession?
    open private(set) var tagSession: Any?
    
    
    
    // MARK: - NFCNDEFTag
    open func read(didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCNDEFReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        let session = NFCNDEFReaderSession(delegate: self,
                                           queue: nil,
                                           invalidateAfterFirstRead: true)
        action = .read
        startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
    }
    
    open func write(message: [String: Any], didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCNDEFReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        if #available(iOS 13.0, *) {
            let session = NFCNDEFReaderSession(delegate: self,
                                               queue: nil,
                                               invalidateAfterFirstRead: false)
            
            let payload = message["payload"] as? String ?? ""
            let type = message["type"] as? String ?? "T"
            let format: NFCTypeNameFormat = (type == "T") ? (.media) : (.absoluteURI)
            
            let payloadData = payload.data(using: .utf8)!
            let typeData = type.data(using: .utf8)!
            let ndefPayload = NFCNDEFPayload(format: format, type: typeData, identifier: Data(), payload: payloadData)
            let ndefMessage = NFCNDEFMessage(records: [ndefPayload])
            
            action = .write(message: ndefMessage)
            startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
        }
    }
    
    open func setMessage(_ alertMessage: String) {
        ndefSession?.alertMessage = alertMessage
    }
    
    
    
    // MARK: - NFCTag
    open func readTag(didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        if #available(iOS 13.0, *) {
            let session = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092],
                                              delegate: self,
                                              queue: nil)!
            action = .read
            startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
        }
    }
    
    open func writeTag(message: [String: Any], didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        if #available(iOS 13.0, *) {
            let session = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092],
                                              delegate: self,
                                              queue: nil)!
            
            let payload = message["payload"] as? String ?? ""
            let type = message["type"] as? String ?? "T"
            let format: NFCTypeNameFormat = (type == "T") ? (.media) : (.absoluteURI)
            
            let payloadData = payload.data(using: .utf8)!
            let typeData = type.data(using: .utf8)!
            let ndefPayload = NFCNDEFPayload(format: format, type: typeData, identifier: Data(), payload: payloadData)
            let ndefMessage = NFCNDEFMessage(records: [ndefPayload])

            action = .write(message: ndefMessage)
            startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
        }
    }
    
    open func setTagMessage(_ alertMessage: String) {
        if #available(iOS 13.0, *) {
            if let session = self.tagSession as? NFCTagReaderSession {
                session.alertMessage = alertMessage
            }
        }
    }
}



// MARK: - NFCNDEFReaderSessionDelegate
extension HiNFCManager : NFCNDEFReaderSessionDelegate {
    
    open func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
        self.didBecomeActive?(self)
    }
    
    open func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        // iOS 13미만
        guard let message = messages.first, let record = message.records.first else {
            self.didDetect?(self, .failure(.invalidated(errorDescription: "NDEF message나 message record가 없습니다.")))
            self.invalidate(errorMessage: "NDEF message나 message record가 없습니다.")
            return
        }
        
        
        let language = String(data: record.payload.advanced(by: 1), encoding: .utf8)
        let encoding = record.payload[0] & NFCTypeNameFormat.nfcWellKnown.rawValue
        let textData = record.payload.advanced(by: 3)
        let text = String(data: textData, encoding: .utf8)
        
        let result: [String: Any] = [
            "Type":record.type.string,
            "Format": self.formattedTNF(from: record.typeNameFormat),
            "Value": [
                "Encoding": "\(encoding)",
                "Language": "\(language ?? "")",
                "Text": "\(text ?? "")"
            ],
            "Raw value": record.payload.string,
            "Payload": "\(record.payload.hexStringFormatted)",
            "Size": "\(record.payload.count)",
        ]
        print("\(String(describing: result.toJsonString()))")
        
        self.didDetect?(self, .success(result))
        self.invalidate(errorMessage: "NDEF message read successfully.")
    }
    
    @available(iOS 13.0, *)
    open func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
        guard tags.count == 1, let tag = tags.first else {
            DispatchQueue.global().asyncAfter(deadline: .now() + .microseconds(500)) {
                session.restartPolling()
            }
            return
        }
        
        session.connect(to: tag) { [weak self] error in
            guard let self = self else { return }
            if error != nil {
                self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                self.invalidate(errorMessage: error?.localizedDescription)
                return
            }
            
            tag.queryNDEFStatus { status, capacity, error in
                switch (status, self.action) {
                case (.notSupported, _):
                    self.didDetect?(self, .failure(.notSupported))
                    self.invalidate(errorMessage: error?.localizedDescription)

                case (.readOnly, _):
                    self.didDetect?(self, .failure(.readOnly))

                case (.readWrite, .read):
                    tag.readNDEF { message, error in
                        if error != nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error?.localizedDescription)
                            return
                        }
                        
                        let record = message?.records.first
                        let result: [String: Any] = [
                            "Type":record?.type.string ?? "",
                            "Format": self.formattedTNF(from: record!.typeNameFormat),
                            "Raw value": record?.payload.string ?? "",
                            "Payload": "\(record?.payload.hexStringFormatted ?? "")",
                            "Size": "\(record?.payload.count ?? 0)",
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }

                case (.readWrite, .write(let message)):
                    guard message.length <= capacity else {
                        self.didDetect?(self, .failure(.invalidPayloadSize))
                        self.invalidate(errorMessage: "Invalid payload size")
                        return
                    }

                    tag.writeNDEF(message) { error in
                        if error != nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error!.localizedDescription)
                            return
                        }
                        let result: [String: Any] = [
                            "result":message.records.first?.payload.string ?? ""
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }
                default:
                    return
                }
            }
        }
    }
    
    open func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        if let error = error as? NFCReaderError,
           error.code != .readerSessionInvalidationErrorFirstNDEFTagRead &&
            error.code != .readerSessionInvalidationErrorUserCanceled {
            self.didDetect?(self, .failure(.invalidated(errorDescription: error.localizedDescription)))
        }
        self.ndefSession = nil
        self.tagSession = nil
        self.didBecomeActive = nil
        self.didDetect = nil
    }
}


// MARK: - Private helper functions
extension HiNFCManager {
    private func invalidate(errorMessage: String?) {
        if errorMessage != nil {
            if #available(iOS 13.0, *) {
                if let ns = ndefSession {
                    ns.invalidate(errorMessage: errorMessage!)
                }
                if let ts = tagSession as? NFCTagReaderSession {
                    ts.invalidate(errorMessage: errorMessage!)
                }
            } else {
                if let ns = ndefSession {
                    ns.invalidate()
                }
                if #available(iOS 13.0, *) {
                    if let ts = tagSession as? NFCTagReaderSession {
                        ts.invalidate()
                    }
                }
            }
        } else {
            if let ns = ndefSession {
                ns.invalidate()
            }
            if #available(iOS 13.0, *) {
                if let ts = tagSession as? NFCTagReaderSession {
                    ts.invalidate()
                }
            }
        }
        ndefSession = nil
        tagSession = nil
        didBecomeActive = nil
        didDetect = nil
    }
    
    private func startSession(session: NFCNDEFReaderSession,
                              didBecomeActive: DidBecomeActive?,
                              didDetect: @escaping DidDetect) {
        self.ndefSession = session
        self.didBecomeActive = didBecomeActive
        self.didDetect = didDetect
        session.begin()
    }
    
    @available(iOS 13.0, *)
    private func startSession(session: NFCTagReaderSession,
                              didBecomeActive: DidBecomeActive?,
                              didDetect: @escaping DidDetect) {
        self.tagSession = session
        self.didBecomeActive = didBecomeActive
        self.didDetect = didDetect
        session.begin()
    }
    
    private func formattedTNF(from tnf: NFCTypeNameFormat) -> String {
        switch tnf {
        case .empty:        // 0: 이름 없는 레코드 (빈 문자열로 표시됨)
            return "Empty (0x00)"
        case .nfcWellKnown: // 1: NFC Forum에서 정의한 레코드 형식
            return "NFC Well Known (0x01)"
        case .media:        // 2: 미디어 타입의 레코드 (ex. 'audio/mp3')
            return "Media (0x02)"
        case .absoluteURI:  // 3: URI 형식의 레코드
            return "Absolute URI (0x03)"
        case .nfcExternal:  // 4: NFC 포럼에서 정의한 외부 레코드
            return "NFC External (0x04)"
        case .unchanged:    // 6: 이름 형식 변경 없음. (현재의 이름 형식을 유지)
            return "Unchanged (0x06)"
        default:            // 5: 알 수 없는 레코드
            return "Unknown (0x05)"
        }
    }
}





// MARK: - NFCTagReaderSessionDelegate
@available(iOS 13.0, *)
extension HiNFCManager : NFCTagReaderSessionDelegate {
    
    public func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
        self.didBecomeActive?(self)
    }
    
    public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
        guard tags.count == 1, let tag = tags.first else {
            DispatchQueue.global().asyncAfter(deadline: .now() + .microseconds(500)) {
                session.restartPolling()
            }
            return
        }
        
        var ndefTag: NFCNDEFTag
        switch tag {
        case let .feliCa(tag):  /// FeliCa tag. (NFCFeliCaTag)
            ndefTag = tag
            self.parseFeliCaTag(tag)
        case let .iso7816(tag): /// ISO14443-4 type A / B tag with ISO7816 communication. (NFCISO7816Tag)
            ndefTag = tag
            self.parseISO7816Tag(tag)
        case let .iso15693(tag):/// ISO15693 tag.
            ndefTag = tag
            self.parseISO15693Tag(tag)
        case let .miFare(tag):  /// MiFare technology tag (MIFARE Plus, UltraLight, DESFire) base on ISO14443. (NFCMiFareTag)
            ndefTag = tag
            self.parseMIFARETag(tag)
        @unknown default:
            self.didDetect?(self, .failure(.invalidated(errorDescription: "Tag not valid.")))
            self.invalidate(errorMessage: "Tag not valid.")
            return
        }
        
        
        session.connect(to: tag) { [weak self] error in
            guard let self = self else { return }
            if error != nil {
                self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                self.invalidate(errorMessage: error?.localizedDescription)
                return
            }
            
            ndefTag.queryNDEFStatus { status, capacity, error in
                switch (status, self.action) {
                case (.notSupported, _):
                    self.didDetect?(self, .failure(.notSupported))
                    self.invalidate(errorMessage: error?.localizedDescription)

                case (.readOnly, _):
                    self.didDetect?(self, .failure(.readOnly))

                case (.readWrite, .read):
                    ndefTag.readNDEF { (message, error) in
                        if error != nil || message == nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error?.localizedDescription)
                            return
                        }

                        let record = message?.records.first
                        let result: [String: Any] = [
                            "Type":record?.type.string ?? "",
                            "Format": self.formattedTNF(from: record!.typeNameFormat),
                            "Raw value": record?.payload.string ?? "",
                            "Payload": "\(record?.payload.hexStringFormatted ?? "")",
                            "Size": "\(record?.payload.count ?? 0)",
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }
                case (.readWrite, .write(let message)):
                    guard message.length <= capacity else {
                        self.didDetect?(self, .failure(.invalidPayloadSize))
                        self.invalidate(errorMessage: "Invalid payload size")
                        return
                    }

                    ndefTag.writeNDEF(message) { error in
                        if error != nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error!.localizedDescription)
                            return
                        }
                        let result: [String: Any] = [
                            "result":message.records.first?.payload.string ?? ""
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }
                default:
                    return
                }
            }
        }
    }
    
    public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
        // NFC 태그 읽기가 중단되었을 때, 호출됩니다.
        print("NFCTag 읽기 중단: \(error.localizedDescription)")
    }
    
}


// MARK: - Private helper functions (for NFCTag)
@available(iOS 13.0, *)
extension HiNFCManager {
    /// FeliCa tag. (NFCFeliCaTag)
    private func parseFeliCaTag(_ tag: NFCFeliCaTag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ FeliCa tag ] ::::::::::
                    "- Type : FeliCa tag."
                    "- currentIDm : \(String(describing: tag.currentIDm))"
                    "- currentSystemCode : \(String(describing: tag.currentSystemCode))"
                    "- description : \(String(describing: tag.description))"
                    ------------------------------------
                    """
        print(log)
    }
    
    /// ISO14443-4 type A / B tag with ISO7816 communication. (NFCISO7816Tag)
    private func parseISO7816Tag(_ tag: NFCISO7816Tag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ ISO7816 tag ] ::::::::::
                    "- Type : ISO14443-4 type A / B tag with ISO7816 communication."
                    "- Identifier : \(String(describing: tag.identifier))"
                    "- historicalBytes : \(String(describing: tag.historicalBytes?.hexStringFormatted))"
                    "- applicationData : \(String(describing: tag.applicationData?.hexStringFormatted))"
                    "- initialSelectedAID : \(tag.initialSelectedAID)"
                    "- proprietaryApplicationDataCoding : \(tag.proprietaryApplicationDataCoding)"
                    "- description : \(String(describing: tag.description))"
                    ------------------------------------
                    """
        print(log)
    }

    /// ISO15693 tag.
    private func parseISO15693Tag(_ tag: NFCISO15693Tag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ ISO15693 tag ] ::::::::::
                    "- Type : ISO15693 tag."
                    "- Identifier : \(tag.identifier.hexString)"
                    "- icSerialNumber : \(String(describing: tag.icSerialNumber.hexStringFormatted))" // IC 시리얼 번호(IC serial number)
                    "- icManufacturerCode : \(String(describing: tag.icManufacturerCode))" // C 제조사 코드(IC manufacturer code)
                    "- description : \(String(describing: tag.description))"
                    ------------------------------------
                    """
        print(log)
    }
    
    /// MiFare technology tag (MIFARE Plus, UltraLight, DESFire) base on ISO14443. (NFCMiFareTag)
    private func parseMIFARETag(_ tag: NFCMiFareTag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ MiFare tag ] ::::::::::
                    "- Type : MiFare technology tag (MIFARE Plus, UltraLight, DESFire) base on ISO14443."
                    "- Identifier : \(tag.identifier.hexString)"
                    "- Historical bytes : \(tag.historicalBytes?.hexString ?? "None")"
                    "- mifareFamily : \(self.formattedMiFareFamily(tag))"
                    "- description : \(String(describing: tag.description))"
                    "- Block count : \(tag.mifareFamily == .plus ? 256 : 16)"
                    ------------------------------------
                    """
        print(log)
    }
    
    private func formattedMiFareFamily (_ tag: NFCMiFareTag) -> String {
        switch (tag.mifareFamily) {
        case .unknown:
            return "MiFare compatible ISO14443 Type A tag." // ISO14443 Type A 호환제품
        case .ultralight:
            return "MiFare Ultralight series."
        case .plus:
            return "MiFare Plus series."
        case .desfire:
            return "MiFare DESFire series."
        default:
            return "Unknown"
        }
    }
}



extension Data {
    // Data([0x12, 0x34, 0x56]) -> 123456
    var hexString: String {
        return map { String(format: "%02hhx", $0) }.joined() // big-endian byte order : reversed().map
    }
    
    // Data([0x12, 0x34, 0x56]) -> 0x12 0x34 0x56
    var hexStringFormatted: String {
        let hexArray = map { String(format: "0x%02hhx", $0) }
        return hexArray.joined(separator: " ")
    }
    
    // Data([0x12, 0x34, 0x56]) -> 313233343536
    public func hexEncodedString() -> String {
        let hexDigits = Array("0123456789abcdef".utf16)
        var hexChars = [UTF16.CodeUnit]()
        hexChars.reserveCapacity(count * 2)
        for byte in self {
            let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
            hexChars.append(hexDigits[index1])
            hexChars.append(hexDigits[index2])
        }
        return String(utf16CodeUnits: hexChars, count: hexChars.count)
    }
    
    var string: String {
        return String(data: self, encoding: .utf8)!
    }
    
    var jsonString: String {
        do {
            let json = try JSONSerialization.jsonObject(with: self, options: [])
            let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
            return String(data: data, encoding: .utf8) ?? ""
        } catch let error {
            print("JSON serialization error: \(error.localizedDescription)")
            return ""
        }
    }
    
    func isJsonString() -> Bool {
        do {
            let _ = try JSONSerialization.jsonObject(with: self, options: [])
            return true
        } catch {
            return false
        }
    }
}

extension Dictionary {
    func toJsonString() -> String? {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted, .sortedKeys])
            return String(data: jsonData, encoding: .utf8)
        } catch {
            print(error.localizedDescription)
            return nil
        }
    }
}

extension Array where Element == Data {
    func toStrings() -> [String] {
        return self.map { String(data: $0, encoding: .utf8) ?? "" }
    }
    
    func toHexStrings() -> [String] {
        return self.map { $0.reduce("") { $0 + String(format: "%02x", $1) } }
    }
}

extension NSObject {
    func rootWindow() -> UIWindow? {
        var window: UIWindow?
        if #available(iOS 15.0, *) {
            window = UIApplication.shared.connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .flatMap { $0.windows }
                .first(where: { $0.isKeyWindow })
        } else {
            window = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
        }
        return window
    }
    
    func showAlert(title: String?, message: String?, actions: [UIAlertAction] = [UIAlertAction(title: "OK", style: .default, handler: nil)], preferredStyle: UIAlertController.Style = .alert) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: preferredStyle)
        for action in actions {
            alertController.addAction(action)
        }
        guard let rootViewController = self.rootWindow()?.rootViewController else { return }
        rootViewController.present(alertController, animated: true)
    }
}

 

사용법 :

// NFC NDEF 테그 쓰기
let str = "테스트 메시지 입니다."
let type = str.hasPrefix("http") == true ? "U" : "T"

let message: [String: Any] = [
    "payload": str,
    "type": type
]
HiNFCManager.shared.write(message: message) { manager in
    manager.setMessage("Place iPhone near the tag to be written on")
} didDetect: { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to write tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag successfully written")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}


// NFC NDEF 테그 읽기
HiNFCManager.shared.read { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to read tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag read successfully")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}




// NFC Tag 쓰기
let str = "https://www.apple.com"
let type = str.hasPrefix("http") == true ? "U" : "T"

let message: [String: Any] = [
    "payload": str,
    "type": type
]
HiNFCManager.shared.writeTag(message: message) { manager in
    manager.setMessage("Place iPhone near the tag to be written on")
} didDetect: { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to write tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag successfully written")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}


// NFC Tag 읽기
HiNFCManager.shared.readTag { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to read tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag read successfully")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}

 

결과값 로그 :

// NFC NDEF tag 읽기결과 로그
{
  "Format" : "Media (0x02)",
  "Payload" : "0x74 0x65 0x73 0x74 0x20 0x6d 0x65 0x73 0x73 0x61 0x67 0x65",
  "Raw value" : "test message",
  "Size" : "12",
  "Type" : "T"
}

// NFC NDEF tag 쓰기결과 로그
{
  "result" : "test message"
}


// NFC Tag 읽기결과 로그
{
  "Format" : "Absolute URI (0x03)",
  "Payload" : "0x68 0x74 0x74 0x70 0x73 ... 0x65 0x2e 0x63 0x6f 0x6d",
  "Raw value" : "https://www.apple.com",
  "Size" : "21",
  "Type" : "U"
}

// NFC Tag 쓰기결과 로그
{
  "result" : "https:\/\/www.apple.com"
}

 

 

2023.04.26 - [iOS] - NFC tag read/write Manager Class (1/2)

2023.04.26 - [iOS] - NFC tag read/write Manager Class (2/2)

 

반응형

'개발 > iOS' 카테고리의 다른 글

XOR 연산을 이용한 문자열 비교  (0) 2023.12.07
Rosetta2  (0) 2023.04.26
NFC tag read/write Manager Class (1/2)  (0) 2023.04.26
Carthage 설치 및 제거  (0) 2023.01.11
NSURLSessionTask 캐싱 비활성화  (0) 2022.10.24
블로그 이미지

SKY STORY

,