অনেক যায়গায় NFC ট্যাগ বা কার্ড ব্যবহার করে। ঐ ট্যাগে থাকা ডেটা সাধারণত নির্দিষ্ট ডিভাইস দিয়ে রিড করা হয়। আমরা চাইলে নিজেরাও যে কোন NFC ট্যাগের ডেটা রিড করতে পারি। যেগুলোতে ডেটা রাইট করলে ক্ষতি হবে না, সেগুলোতে রাইট করতে পারি।
অনেক ধরণের NFC কার্ড রয়েছে। এক এক কার্ড বা ট্যাগ একেক স্ট্যান্ডার্ড ফলো করে। যেমন iso15693, iso14443, iso18092 ইত্যাদি। প্রতিটা ট্যাগের একটা করে আইডি থাকে। আমরা প্রথমে দেখি কিভাবে রেগুলার NFC আইডি কার্ড রিড করা যায়। তার জন্য একটা SwiftUI অ্যাপ তৈরি করে নিব Xcode এ।
NFC রিড করার জন্য অ্যাপ টার্গেটের Signing & Capabilities থেকে Near Field Communication Tag Reading যুক্ত করতে হবে।
এছাড়া NFC স্ক্যান করার জন্য Targets > Info থেকে Privacy- NFC Scan Usage Description যোগ করতে হবে। “We need to use NFC to read tags” স্ট্রিং ভ্যালু যোগ করতে পারেন।
Info.plist এ সরাসরি এই কি যোগ করতে পারিঃ
<key>NFCReaderUsageDescription</key>
<string>This app uses NFC to read tag data.</string>
এরপর ContentView.swift ফাইলে নিচের মত করে কোড লিখিঃ
import SwiftUI
import CoreNFC
struct ContentView: View {
@StateObject private var nfcManager = NFCReaderManager.shared
var body: some View {
VStack(spacing: 20) {
Text(nfcManager.nfcMessage)
.padding()
.font(.headline)
.multilineTextAlignment(.center)
if let errorMessage = nfcManager.errorMessage {
Text("Error: \(errorMessage)")
.foregroundColor(.red)
.padding()
}
Button(action: nfcManager.startSession) {
Text(nfcManager.isScanning ? "Scanning..." : "Start NFC Scan")
.frame(maxWidth: .infinity)
.padding()
.background(nfcManager.isScanning ? Color.gray : Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding()
.disabled(nfcManager.isScanning)
}
.padding()
}
}
class NFCReaderManager: NSObject, NFCNDEFReaderSessionDelegate, ObservableObject {
static let shared = NFCReaderManager()
@Published var nfcMessage: String = "Tap to start scanning for NFC tags"
@Published var isScanning: Bool = false
@Published var errorMessage: String?
private var session: NFCNDEFReaderSession?
func startSession() {
guard NFCReaderSession.readingAvailable else {
errorMessage = "NFC is not supported on this device"
return
}
isScanning = true
errorMessage = nil
session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
session?.alertMessage = "Hold your iPhone near the NFC tag"
session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
let nfcError = error as NSError
// Suppress the "First NDEF tag read" error
if nfcError.code == NFCReaderError.readerSessionInvalidationErrorFirstNDEFTagRead.rawValue {
// Expected behavior; no further action required.
DispatchQueue.main.async {
self.isScanning = false
}
return
}
DispatchQueue.main.async {
self.errorMessage = error.localizedDescription
self.isScanning = false
}
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
guard let firstMessage = messages.first else {
DispatchQueue.main.async {
self.nfcMessage = "No NDEF message found."
self.isScanning = false
}
return
}
var result = ""
for record in firstMessage.records {
if let string = String(data: record.payload, encoding: .utf8) {
result.append(string)
}
}
DispatchQueue.main.async {
self.nfcMessage = result
self.isScanning = false
}
}
}
উপরের কোড দিয়ে আমরা রেগুলার NFC কার্ড রিড করতে পারব।
NFCReaderManager ক্লাসে startSession() মেথড লিখেছি। যে মেথড কল করলে NFC সেশন চালু করবে। ডিভাইস NFC ট্যাগ রিড করবে। সেশনে কি কি ট্যাগ রিড করবে, তা বলে দিতে হবে। pollingOption: [.iso14443, .iso15693, .iso18092] এ আমরা বলে দিয়েছি এই ট্যাগ গুলো রিড করার জন্য।
Delegate Methods:
readerSession(_:didInvalidateWithError:)
: NFC রিড করার সময় যদি কোন রিড করা ক্লোজ করা হয়, তাহলে এই মেথড কল হবে। এটা ইউজারও কল করতে পারে।- readerSession(_:didDetectNDEFs:): ট্যাগ ডিটেক্ট করলে এই মেথড কল হবে। এই মেথডে আমরা ট্যাগ ডেটা রিড করতে পারব। উপরের উদাহরণে আমরা শুধু মাত্র NDEF ট্যাগ রিড করেছি। একেক কার্ডের একেক স্ট্যান্ডার্ড। তাই একেক কার্ড একেক ভাবে রিড করতে হবে।
ভিন্ন ধরণের NFC ট্যাগ বা কার্ড রিড করা
উপরে শুধু মাত্র NDEF টাইপ ট্যাগ রিড করতে পেরেছি। এছাড়া আরো অনেক ধরণের NFC ট্যাগ রয়েছে। সেগুলো রিড করার জন্য NFCTagReaderSession লাগবে। আর তাই NFCReaderManager ক্লাসে NFCTagReaderSession কে কনফর্ম করতে হবে। ঐ ক্লাসে tagReaderSessionDidBecomeActive, tagReaderSession, tagReaderSession এই তিনটা মেথড ইমপ্লিমেন্ট করতে হবে। সম্পূর্ণ কোড দিচ্ছি, এরপর ব্যাখ্যা করছিঃ
import SwiftUI
import CoreNFC
struct ContentView: View {
@StateObject private var nfcManager = NFCReaderManager.shared
var body: some View {
VStack(spacing: 20) {
Text(nfcManager.nfcMessage)
.padding()
.font(.headline)
.multilineTextAlignment(.center)
if let errorMessage = nfcManager.errorMessage {
Text("Error: \(errorMessage)")
.foregroundColor(.red)
.padding()
}
Button(action: nfcManager.startTagReaderSession) {
Text(nfcManager.isReadingNFC ? "Reading NFC..." : "Read NFC Tag")
.frame(maxWidth: .infinity)
.padding()
.background(nfcManager.isReadingNFC ? Color.gray : Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
.padding()
.disabled(nfcManager.isReadingNFC)
}
.padding()
}
}
class NFCReaderManager: NSObject, NFCTagReaderSessionDelegate, ObservableObject {
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
}
static let shared = NFCReaderManager()
@Published var nfcMessage: String = "Tap to start reading NFC tags"
@Published var isReadingNFC: Bool = false
@Published var errorMessage: String?
private var tagSession: NFCTagReaderSession?
func startTagReaderSession() {
guard NFCTagReaderSession.readingAvailable else {
errorMessage = "NFC is not supported on this device"
return
}
isReadingNFC = true
errorMessage = nil
tagSession = NFCTagReaderSession(pollingOption: [
.iso14443, // For MIFARE and ISO14443 tags
.iso15693, // For ISO15693 tags
.iso18092 // For FeliCa tags
], delegate: self, queue: nil)
tagSession?.alertMessage = "Hold your iPhone near the NFC tag"
tagSession?.begin()
}
// Delegate method called when NFC tags are detected
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
guard let firstTag = tags.first else {
session.invalidate(errorMessage: "No tags detected.")
self.isReadingNFC = false
return
}
session.connect(to: firstTag) { error in
if let error = error {
DispatchQueue.main.async {
self.nfcMessage = "Error connecting to tag: \(error.localizedDescription)"
session.invalidate(errorMessage: "Connection failed.")
self.isReadingNFC = false
}
return
}
DispatchQueue.main.async {
// Handle tag types
switch firstTag {
case .miFare(let tag):
self.nfcMessage = "MiFare tag detected with identifier: \(tag.identifier.map { String(format: "%.2hhX", $0) }.joined())"
case .iso7816(let tag):
self.nfcMessage = "ISO7816 tag detected with identifier: \(tag.identifier.map { String(format: "%.2hhX", $0) }.joined())"
case .iso15693(let tag):
self.nfcMessage = "ISO15693 tag detected with identifier: \(tag.identifier.map { String(format: "%.2hhX", $0) }.joined())"
case .feliCa(let tag):
self.nfcMessage = "FeliCa tag detected with identifier: \(tag.currentIDm.map { String(format: "%.2hhX", $0) }.joined())"
default:
self.nfcMessage = "Unknown tag detected."
}
// Automatically close the session after a successful read
session.invalidate()
self.isReadingNFC = false
}
}
}
// Delegate method called when the session is invalidated
func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
DispatchQueue.main.async {
// If the session was intentionally invalidated, avoid showing an error
let nfcError = error as NSError
if nfcError.code == NFCReaderError.readerSessionInvalidationErrorUserCanceled.rawValue {
self.errorMessage = nil
} else {
self.errorMessage = error.localizedDescription
}
self.isReadingNFC = false
}
}
}
এছাড়া Info.plist এ কি ধরনের ট্যাগ আমরা রিড করব, তার এনটাইটেলমেন্ট গুলো যোগ করতে হবে:
<?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>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>com.apple.developer.nfc.readersession.iso7816</key>
<array>
<!-- for ePassport-->
<string>A0000002471001</string>
</array>
<key>com.apple.developer.nfc.readersession.iso15693</key>
<array>
<string>A</string>
</array>
<!-- for felica tags-->
<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
<string>12FC</string>
<!-- bd rapid or mrt pass -->
<string>90E3</string>
</array>
</dict>
</plist>
এখন অ্যাপ রান করে আমরা NFC ট্যাগ অথবা ইপাসপোর্ট রিড করতে পারব। যা নির্দিষ্ট ট্যাগের আইডি দেখাবে।