Swift

[iOS] FNC TAG_READER_TEST_ZIZI

ziziDev 2025. 11. 11. 21:35
반응형
 
/*
See the LICENSE.txt file for this sample’s licensing information.

Abstract:
The view controller that scans and displays NDEF messages.
*/

import UIKit
import CoreNFC

/// - Tag: MessagesTableViewController
class MessagesTableViewController: UITableViewController, NFCNDEFReaderSessionDelegate, NFCTagReaderSessionDelegate {

// MARK: - Properties

let reuseIdentifier = "reuseIdentifier"
var detectedMessages = [NFCNDEFMessage]()
var session: NFCNDEFReaderSession?
var tagSession: NFCTagReaderSession?
var detectedTagInfo = [String]() // 일반 태그 정보 저장
 
// MARK: - View Lifecycle
 
override func viewDidLoad() {
super.viewDidLoad()
 
// 왼쪽에 "카드 스캔" 버튼 추가
let cardScanButton = UIBarButtonItem(title: "카드 스캔", style: .plain, target: self, action: #selector(beginGeneralTagScanning(_:)))
navigationItem.leftBarButtonItem = cardScanButton
 
// 오른쪽 "Scan" 버튼은 Storyboard에 이미 있음
}

// MARK: - Actions

/// - Tag: beginScanning
@IBAction func beginScanning(_ sender: Any) {
guard NFCNDEFReaderSession.readingAvailable else {
let alertController = UIAlertController(
title: "Scanning Not Supported",
message: "This device doesn't support tag scanning.",
preferredStyle: .alert
)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
return
}

session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
session?.alertMessage = "Hold your iPhone near the item to learn more about it."
session?.begin()
}

// MARK: - NFCNDEFReaderSessionDelegate

/// - Tag: processingTagData
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
DispatchQueue.main.async {
// Process detected NFCNDEFMessage objects.
self.detectedMessages.append(contentsOf: messages)
self.tableView.reloadData()
}
}

/// - Tag: processingNDEFTag
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
if tags.count > 1 {
// Restart polling in 500ms
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
}
 
// Connect to the found tag and perform NDEF message reading
let tag = tags.first!
session.connect(to: tag, completionHandler: { (error: Error?) in
if nil != error {
session.alertMessage = "Unable to connect to tag."
session.invalidate()
return
}
 
tag.queryNDEFStatus(completionHandler: { (ndefStatus: NFCNDEFStatus, capacity: Int, error: Error?) in
if .notSupported == ndefStatus {
session.alertMessage = "Tag is not NDEF compliant"
session.invalidate()
return
} else if nil != error {
session.alertMessage = "Unable to query NDEF status of tag"
session.invalidate()
return
}
 
tag.readNDEF(completionHandler: { (message: NFCNDEFMessage?, error: Error?) in
var statusMessage: String
if nil != error || nil == message {
statusMessage = "Fail to read NDEF from tag"
} else {
statusMessage = "Found 1 NDEF message"
DispatchQueue.main.async {
// Process detected NFCNDEFMessage objects.
self.detectedMessages.append(message!)
self.tableView.reloadData()
}
}
 
session.alertMessage = statusMessage
session.invalidate()
})
})
})
}
 
/// - Tag: sessionBecomeActive
func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
 
}
 
/// - Tag: endScanning
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Check the invalidation reason from the returned error.
if let readerError = error as? NFCReaderError {
// Show an alert when the invalidation reason is not because of a
// successful read during a single-tag read session, or because the
// user canceled a multiple-tag read session from the UI or
// programmatically using the invalidate method call.
if (readerError.code != .readerSessionInvalidationErrorFirstNDEFTagRead)
&& (readerError.code != .readerSessionInvalidationErrorUserCanceled) {
let alertController = UIAlertController(
title: "Session Invalidated",
message: error.localizedDescription,
preferredStyle: .alert
)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
DispatchQueue.main.async {
self.present(alertController, animated: true, completion: nil)
}
}
}

// To read new tags, a new session instance is required.
self.session = nil
}

// MARK: - addMessage(fromUserActivity:)

func addMessage(fromUserActivity message: NFCNDEFMessage) {
DispatchQueue.main.async {
self.detectedMessages.append(message)
self.tableView.reloadData()
}
}
 
// MARK: - General Tag Reading (ISO7816, MiFare, FeliCa)
 
@IBAction func beginGeneralTagScanning(_ sender: Any) {
guard NFCTagReaderSession.readingAvailable else {
let alertController = UIAlertController(
title: "Scanning Not Supported",
message: "This device doesn't support tag scanning.",
preferredStyle: .alert
)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
return
}
 
tagSession = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092], delegate: self)
tagSession?.alertMessage = "카드나 태그를 iPhone 상단에 가까이 대주세요."
tagSession?.begin()
}
 
// MARK: - NFCTagReaderSessionDelegate
 
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
print("Tag reader session became active")
}
 
func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
if let readerError = error as? NFCReaderError {
if (readerError.code != .readerSessionInvalidationErrorFirstNDEFTagRead)
&& (readerError.code != .readerSessionInvalidationErrorUserCanceled) {
DispatchQueue.main.async {
let alertController = UIAlertController(
title: "세션 종료",
message: error.localizedDescription,
preferredStyle: .alert
)
alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
}
self.tagSession = nil
}
 
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
if tags.count > 1 {
session.alertMessage = "태그가 2개 이상 감지되었습니다. 하나만 남겨주세요."
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(500)) {
session.restartPolling()
}
return
}
 
let tag = tags.first!
session.connect(to: tag) { (error: Error?) in
if error != nil {
session.invalidate(errorMessage: "태그에 연결할 수 없습니다.")
return
}
 
var tagInfo = ""
var tagType = ""
 
switch tag {
case .iso7816(let iso7816Tag):
tagType = "ISO7816 (신용카드/스마트카드)"
tagInfo = self.readISO7816Tag(iso7816Tag)
 
case .miFare(let miFareTag):
tagType = "MiFare (출입카드/교통카드)"
tagInfo = self.readMiFareTag(miFareTag)
 
case .feliCa(let feliCaTag):
tagType = "FeliCa (교통카드)"
tagInfo = self.readFeliCaTag(feliCaTag)
 
case .iso15693(let iso15693Tag):
tagType = "ISO15693"
tagInfo = self.readISO15693Tag(iso15693Tag)
 
@unknown default:
tagType = "Unknown Tag Type"
tagInfo = "알 수 없는 태그 타입입니다."
}
 
let fullInfo = "[\(tagType)]\n\(tagInfo)"
 
DispatchQueue.main.async {
// NDEF 메시지 형태로 변환하여 표시
let record = NFCNDEFPayload(
format: .nfcWellKnown,
type: "T".data(using: .utf8)!,
identifier: Data(),
payload: fullInfo.data(using: .utf8)!
)
let message = NFCNDEFMessage(records: [record])
self.detectedMessages.insert(message, at: 0)
self.tableView.reloadData()
}
 
session.alertMessage = "태그 읽기 완료"
session.invalidate()
}
}
 
// MARK: - Tag Reading Helpers
 
private func readISO7816Tag(_ tag: NFCISO7816Tag) -> String {
var info = ""
info += "UID: \(tag.identifier.map { String(format: "%02X", $0) }.joined(separator: " "))\n"
info += "Historical Bytes: \(tag.historicalBytes?.map { String(format: "%02X", $0) }.joined(separator: " ") ?? "없음")\n"
info += "Application Data: \(tag.applicationData?.map { String(format: "%02X", $0) }.joined(separator: " ") ?? "없음")\n"
info += "\n* 암호화된 데이터는 읽을 수 없습니다"
return info
}
 
private func readMiFareTag(_ tag: NFCMiFareTag) -> String {
var info = ""
info += "UID: \(tag.identifier.map { String(format: "%02X", $0) }.joined(separator: " "))\n"
 
switch tag.mifareFamily {
case .plus:
info += "타입: MiFare Plus\n"
case .ultralight:
info += "타입: MiFare Ultralight\n"
case .desfire:
info += "타입: MiFare DESFire\n"
case .unknown:
info += "타입: Unknown MiFare\n"
@unknown default:
info += "타입: Unknown\n"
}
 
info += "Historical Bytes: \(tag.historicalBytes?.map { String(format: "%02X", $0) }.joined(separator: " ") ?? "없음")\n"
info += "\n* 출입카드/교통카드 데이터는 암호화되어 있습니다"
return info
}
 
private func readFeliCaTag(_ tag: NFCFeliCaTag) -> String {
var info = ""
info += "UID: \(tag.currentIDm.map { String(format: "%02X", $0) }.joined(separator: " "))\n"
info += "System Code: \(tag.currentSystemCode.map { String(format: "%02X", $0) }.joined(separator: " "))\n"
info += "\n* 교통카드 잔액 등은 암호화되어 읽을 수 없습니다"
return info
}
 
private func readISO15693Tag(_ tag: NFCISO15693Tag) -> String {
var info = ""
info += "UID: \(tag.identifier.map { String(format: "%02X", $0) }.joined(separator: " "))\n"
info += "IC Manufacturer Code: \(String(format: "%02X", tag.icManufacturerCode))\n"
info += "IC Serial Number: \(tag.icSerialNumber.map { String(format: "%02X", $0) }.joined(separator: " "))\n"
return info
}
}


-------

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>NDEF</string>
<string>TAG</string>
<string>ISO7816</string>
<string>FELICA</string>
</array>
</dict>
</plist>

 

 

 

 

--------

 

 

반응형