Closed JoeKaldas closed 3 years ago
Hello, I’m also in need to this update.
Hello, I’m also in need to this update.
I managed to do it in the end for iOS using a native approach (swift) with flutter, using only this library for Android
Did it worked?
Yes, it worked.
Flutter code
Future<void> getNFCTag() async {
String message;
try {
var result = await platform.invokeMethod('getNFCTag');
message = result;
} on PlatformException catch (e) {
message = "Error: ${e.message}'.";
}
}
Add this to appdelegate: NFCTagReaderSessionDelegate
Add this inside didFinishLaunchingWithOptions
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let nfcChannel = FlutterMethodChannel(name: "samples.flutter.dev/nfc", binaryMessenger: controller.binaryMessenger)
nfcChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
guard call.method == "getNFCTag" else {
result(FlutterMethodNotImplemented)
return
}
self?.startReadingNFC(result: result)
})
And those outside `didFinishLaunchingWithOptions`
func hexEncodedString(byteArray: [UInt8]) -> String {
let hexDigits = Array("0123456789abcdef".utf16)
var hexChars = [UTF16.CodeUnit]()
hexChars.reserveCapacity(byteArray.count * 2)
for byte in byteArray {
let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
hexChars.append(hexDigits[index1])
hexChars.append(hexDigits[index2])
}
return String(utf16CodeUnits: hexChars, count: hexChars.count)
}
private func startReadingNFC(result: @escaping FlutterResult) {
flutterResult = result
session = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main)
session?.begin()
}
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
}
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
if case let NFCTag.miFare(tag) = tags.first! {
session.connect(to: tags.first!) { (error: Error?) in
let tagUIDData = tag.identifier
var byteData: [UInt8] = []
tagUIDData.withUnsafeBytes { byteData.append(contentsOf: $0) }
session.invalidate()
DispatchQueue.main.async {
flutterResult(self.hexEncodedString(byteArray: byteData))
}
}
}
}
func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
DispatchQueue.main.async {
session.invalidate()
print(error.localizedDescription)
flutterResult(error.localizedDescription)
}
}
THANK YOU SO MUCH!
THANK YOU SO MUCH!
Did it work for you?
Hey Joe,
Trying to implement this now, thank you so much for sharing this! However, I'm running into a syntax error in xcode surrounding flutterResult. Do i need to define that somewhere in the AppDelegate.swift file or even import a library?
If you want to share screenshots of your code so I can follow along, that would be amazing!
thank you!
i'm getting a "cannot find 'flutterResult' in scope error"
Got it working! Added "var flutterResult: FlutterResult! var session: NFCTagReaderSession!" to the class & that fixed the syntax errors. Also shared a screenshot from my AppDelegate.swift in case anyone else has any issues.
@diarmuidmcg Thanks for the screenshot, I'm still somehow getting errors:
error: reference to property 'flutterResult' in closure requires explicit use of 'self' to make capture semantics explicit error: cannot find 'controller' in scope
any ideas on how to fix them?
yes! include this line inside didFinishLaunchingWithOptions:
let controller = window?.rootViewController as! FlutterViewController
Hey @diarmuidmcg I'll send you a screenshot tonight.
I did both approaches for Swift.
@diarmuidmcg @JoeKaldas Thank you so much, everything works now. Could you share how to get both the tag ID and the payload?
@hanabon Yes, I'll share it as soon as I'm home tonight.
Here is the code for detecting payload/message (I'm only detecting first message only) Code for detecting tag ID is after
import UIKit
import Flutter
import CoreNFC
var session: NFCNDEFReaderSession?
var flutterResult: FlutterResult!
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, NFCNDEFReaderSessionDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let nfcChannel = FlutterMethodChannel(name: "samples.flutter.dev/nfc", binaryMessenger: controller.binaryMessenger)
nfcChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
guard call.method == "getNFCTag" else {
result(FlutterMethodNotImplemented)
return
}
self?.startReadingNFC(result: result)
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func hexEncodedString(byteArray: [UInt8]) -> String {
let hexDigits = Array("0123456789abcdef".utf16)
var hexChars = [UTF16.CodeUnit]()
hexChars.reserveCapacity(byteArray.count * 2)
for byte in byteArray {
let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
hexChars.append(hexDigits[index1])
hexChars.append(hexDigits[index2])
}
return String(utf16CodeUnits: hexChars, count: hexChars.count)
}
private func startReadingNFC(result: @escaping FlutterResult) {
flutterResult = result
session = NFCNDEFReaderSession(delegate: self, queue: DispatchQueue.main, invalidateAfterFirstRead: true)
session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
if (tags.count > 0) {
session.connect(to: tags.first!, completionHandler: { (connection_error) in
tags.first?.readNDEF(completionHandler: { (message, error) in
if ((message?.records.count)! > 0) {
if let dataMessage = String(data: (message?.records.first!.payload)!, encoding:.utf8) {
session.invalidate()
DispatchQueue.main.async {
flutterResult(dataMessage)
}
}
}
})
})
}
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
session.invalidate()
DispatchQueue.main.async {
flutterResult(error.localizedDescription)
}
}
}
Here is the code for detecting the tag ID:
import UIKit
import Flutter
import CoreNFC
var session_tag: NFCTagReaderSession?
var flutterResult: FlutterResult!
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, NFCTagReaderSessionDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let nfcChannel = FlutterMethodChannel(name: "samples.flutter.dev/nfc", binaryMessenger: controller.binaryMessenger)
nfcChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
guard call.method == "getNFCTag" else {
result(FlutterMethodNotImplemented)
return
}
self?.startReadingNFC(result: result)
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func hexEncodedString(byteArray: [UInt8]) -> String {
let hexDigits = Array("0123456789abcdef".utf16)
var hexChars = [UTF16.CodeUnit]()
hexChars.reserveCapacity(byteArray.count * 2)
for byte in byteArray {
let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
hexChars.append(hexDigits[index1])
hexChars.append(hexDigits[index2])
}
return String(utf16CodeUnits: hexChars, count: hexChars.count)
}
private func startReadingNFC(result: @escaping FlutterResult) {
flutterResult = result
session_tag = NFCTagReaderSession(pollingOption: .iso14443, delegate: self, queue: DispatchQueue.main)
session_tag?.begin()
}
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
print("Tag session active")
}
func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
print("Did detect tag reader session")
print(tags.count)
if case let NFCTag.miFare(tag) = tags.first! {
session.connect(to: tags.first!) { (error: Error?) in
let tagUIDData = tag.identifier
var byteData: [UInt8] = []
tagUIDData.withUnsafeBytes { byteData.append(contentsOf: $0) }
session.invalidate()
DispatchQueue.main.async {
flutterResult(self.hexEncodedString(byteArray: byteData))
}
}
}
}
func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
print("Tag Error: \(error.localizedDescription)")
DispatchQueue.main.async {
session.invalidate()
print(error.localizedDescription)
flutterResult(error.localizedDescription)
}
}
}
Hello All, Thank you so much for your effort I really appreciate. I'm new to this, can anybody tell me how to combine both codes to get the id and payload at the same time? As I can't combine the getter for id with the normal nfc_flutter package. Any help?
Or how to use it with the package? as when i combine both your code and the package only one session at a time works and when I try to access the package code the invokedMethod is hidden. Also being able to show a text on the reading pop up through your method will be appreciated. Thank you
Guys please
Hello @mazensabouni ,
For Android using this library, you have
tag.id
and tag.content
For iOS, you should be able to use same thing.
I ran into the issue when trying to detect tag ID on iOS because I was using certain Mifare version that wasn't supported so had to do this workaround.
Hi there, thanks for the solution. I did not understand a thing.
I put the code you provided for reading the tag id in AppDelegate.swift (ios/runner folder), but I did not understand how to call it from my flutter function inside a .dart file.
Can you help me? Thanks!
@JoeKaldas I think I have to ask this to you
@PiroX4256
So in AppDelegate. you added the following
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let nfcChannel = FlutterMethodChannel(name: "samples.flutter.dev/nfc", binaryMessenger: controller.binaryMessenger)
nfcChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
// Note: this method is invoked on the UI thread.
guard call.method == "getNFCTag" else {
result(FlutterMethodNotImplemented)
return
}
self?.startReadingNFC(result: result)
})
Notice the "getNFCTag" part, this is the name of your function that you will define in Flutter.
So in your flutter app, you can have the following, that you call upon clicking "scan" for example
Future<void> getNFCTag() async {
String message;
try {
var result = await platform.invokeMethod('getNFCTag');
message = result;
} on PlatformException catch (e) {
message = "Error: ${e.message}'.";
}
}
Ok, now it opens the iOS tag scanning windows. The problem is that it does not detect my tag, which is a Mifare Classic 1k. Do you have an idea on how make it detecting my card?
@PiroX4256 Yes, I think you need to write data on the card first. So in my case, I scanned the card using any NFC app "NFC Tools" for example, then write the detected tag on the card.
@JoeKaldas I have written a plain text field on my card, but my phone still does not recognize it. It works with nfc tool but only in compatibility mode. For my purposes I only need to extract tag id from my nfc tag. Is it possible? Thanks again
@PiroX4256 I think this was my issue and I did a workaround by writing the card's tag id on the card itself.
Oh well, okay. Thank you.
Hello there! Guys, I need your help, please.
What do I need: to get the tag's UID using iOS app What do I have: I have an app Flutter, which works fine on Android (I can read the tag's UID) and do nothing on iOS
Details:
I have this type of cards (plastic card)
As you can see, this is Mifare Classic 1k card with payload "My text with spaces"
I used flutter channels to communicate between flutter and iOS native (swift)
On iOS side I created two implementations: one for NFCTagReaderSession, another one for NFCNDEFReaderSession (I do not use it at the same time, only separated) Both approaches start fine and open the iOS's window with "read a tag", but with NFCTagReaderSession nothing is happening. It seems like there is no card. With NFCNDEFReaderSession implementation, I can read the message "My text with spaces" but, of course, I can't read the tag's UID
What i'm doing wrong?
Hello, I'm trying to retrieve the tag ID on iOS but always returns an empty string. It returns the correct ID on Android but nothing on iOS. I know this was mentioned before and there should be an update about it