Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ios ISO15693 extended read/write commands (0x30 and 0x31) #117

Merged
merged 7 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Simple example:

```dart
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:flutter_nfc_kit/iso15963flags.dart';
import 'package:ndef/ndef.dart' as ndef;

var availability = await FlutterNfcKit.nfcAvailability;
Expand All @@ -68,7 +69,7 @@ if (tag.type == NFCTagType.iso7816) {
await FlutterNfcKit.setIosAlertMessage("hi there!");

// read NDEF records if available
if (tag.ndefAvailable){
if (tag.ndefAvailable) {
/// decoded NDEF records (see [ndef.NDEFRecord] for details)
/// `UriRecord: id=(empty) typeNameFormat=TypeNameFormat.nfcWellKnown type=U uri=https://github.com/nfcim/ndef`
for (var record in await FlutterNfcKit.readNDEFRecords(cached: false)) {
Expand All @@ -89,6 +90,20 @@ if (tag.ndefWritable) {
await FlutterNfcKit.writeNDEFRawRecords([new NDEFRawRecord("00", "0001", "0002", "0003", ndef.TypeNameFormat.unknown)]);
}

// Transceive ISO15693 commands (iOS only)
final Set<Iso15693RequestFlag> flags = {Iso15693RequestFlag.highDataRate};
// Transceive ISO15693 0x31 command
await FlutterNfcKit.extendedWriteSingleBlock(
requestFlags: flags,
blockNumber: 0,
dataBlock: [0x06, 0x16, 0x00, 0x00],
);

// Transceive ISO15693 0x30 command
Uint8List chunk = await FlutterNfcKit.extendedReadSingleBlock(
requestFlags: flags, blockNumber: 64);


// Call finish() only once
await FlutterNfcKit.finish();
// iOS only: show alert/error message on finish
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# platform :ios, '11.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
6 changes: 3 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_nfc_kit/ios"

SPEC CHECKSUMS:
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_nfc_kit: 965c98c3fa68f5609f1cc89abb968fe1b8ffdbaa

PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3

COCOAPODS: 1.11.2
COCOAPODS: 1.13.0
13 changes: 8 additions & 5 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -165,7 +165,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
Expand Down Expand Up @@ -228,10 +228,12 @@
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
Expand All @@ -242,6 +244,7 @@
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
Expand Down Expand Up @@ -351,7 +354,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -436,7 +439,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -485,7 +488,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
4 changes: 4 additions & 0 deletions example/ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,9 @@
<array>
<string>A00000000386980701</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
97 changes: 75 additions & 22 deletions ios/Classes/SwiftFlutterNfcKitPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extension Data {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}

func hexEncodedString(options: HexEncodingOptions = [.upperCase]) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return map { String(format: format, $0) }.joined()
Expand All @@ -35,13 +35,13 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
var result: FlutterResult?
var tag: NFCTag?
var multipleTagMessage: String?

public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_nfc_kit", binaryMessenger: registrar.messenger())
let instance = SwiftFlutterNfcKitPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}

// from FlutterPlugin
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "getNFCAvailability" {
Expand Down Expand Up @@ -90,14 +90,14 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
default:
data = nil
}

switch tag {
case let .iso7816(tag):
var apdu: NFCISO7816APDU?
if data != nil {
apdu = NFCISO7816APDU(data: data!)
}

if apdu != nil {
tag.sendCommand(apdu: apdu!, completionHandler: { (response: Data, sw1: UInt8, sw2: UInt8, error: Error?) in
if let error = error {
Expand Down Expand Up @@ -159,6 +159,41 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
} else {
result(FlutterError(code: "406", message: "No tag polled", details: nil))
}
} else if call.method == "Iso15693extendedReadSingleBlock" {
let arguments = call.arguments as! [String : Any?]
let requestFlags = getRequestFlags(arguments["requestFlags"] as! [String])
let blockNumber = arguments["blockNumber"] as! Int

if case let .iso15693(tag) = tag {
tag.extendedReadSingleBlock(requestFlags: requestFlags, blockNumber: blockNumber) { dataBlock, error in
if let error = error {
result(self.wrapFlutterError(error))
} else {
result(dataBlock)
}
}
} else {
result(FlutterError(code: "405", message: "ISO15693 Extended Read Single Block not supported on this type of card", details: nil))
}
} else if call.method == "Iso15693extendedWriteSingleBlock" {
let arguments = call.arguments as! [String : Any?]
let requestFlags = getRequestFlags(arguments["requestFlags"] as! [String])
let blockNumber = arguments["blockNumber"] as! Int
let dataBlock = (arguments["dataBlock"] as! FlutterStandardTypedData).data

let adjDataBlock = [UInt8](dataBlock);

if case let .iso15693(tag) = tag {
tag.extendedWriteSingleBlock(requestFlags: requestFlags, blockNumber: blockNumber, dataBlock: dataBlock) { error in
if let error = error {
result(self.wrapFlutterError(error))
} else {
result(nil)
}
}
} else {
result(FlutterError(code: "405", message: "ISO15693 Extended Write Single Block not supported on this type of card", details: nil))
}
} else if call.method == "readNDEF" {
if tag != nil {
var ndefTag: NFCNDEFTag?
Expand All @@ -183,10 +218,10 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
result(FlutterError(code: "500", message: "Read NDEF error", details: error.localizedDescription))
} else if let msg = msg {
var records: [[String: Any]] = []

for record in msg.records {
var entry: [String: Any] = [:]

entry["identifier"] = record.identifier.hexEncodedString()
entry["payload"] = record.payload.hexEncodedString()
entry["type"] = record.type.hexEncodedString()
Expand All @@ -206,10 +241,10 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
default:
entry["typeNameFormat"] = "unknown"
}

records.append(entry)
}

let jsonData = try! JSONSerialization.data(withJSONObject: records)
let jsonString = String(data: jsonData, encoding: .utf8)
result(jsonString)
Expand Down Expand Up @@ -269,7 +304,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
payload: dataWithHexString(hex: record["payload"] as! String)
))
}

ndefTag!.writeNDEF(NFCNDEFMessage(records: records), completionHandler: { (error: Error?) in
if let error = error {
result(FlutterError(code: "500", message: "Write NDEF error", details: error.localizedDescription))
Expand All @@ -289,12 +324,12 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
} else if call.method == "finish" {
self.result?(FlutterError(code: "406", message: "Session not active", details: nil))
self.result = nil

if let session = session {
let arguments = call.arguments as! [String: Any?]
let alertMessage = arguments["iosAlertMessage"] as? String
let errorMessage = arguments["iosErrorMessage"] as? String

if let errorMessage = errorMessage {
session.invalidate(errorMessage: errorMessage)
} else {
Expand All @@ -305,7 +340,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
self.session = nil
}

tag = nil
result(nil)
} else if call.method == "setIosAlertMessage" {
Expand Down Expand Up @@ -351,10 +386,10 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
result(FlutterMethodNotImplemented)
}
}

// from NFCTagReaderSessionDelegate
public func tagReaderSessionDidBecomeActive(_: NFCTagReaderSession) {}

// from NFCTagReaderSessionDelegate
public func tagReaderSession(_: NFCTagReaderSession, didInvalidateWithError error: Error) {
guard result != nil else { return; }
Expand All @@ -378,7 +413,25 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
session = nil
tag = nil
}


@available(iOS 13.0, *)
func getRequestFlags(_ arg: [String]) -> RequestFlag {
var flag = RequestFlag()
if arg.contains("address") { flag.insert(RequestFlag.address) }
if arg.contains("dualSubCarriers") { flag.insert(RequestFlag.dualSubCarriers) }
if arg.contains("highDataRate") { flag.insert(RequestFlag.highDataRate) }
if arg.contains("option") { flag.insert(RequestFlag.option) }
if arg.contains("protocolExtension") { flag.insert(RequestFlag.protocolExtension) }
if arg.contains("select") { flag.insert(RequestFlag.select) }
return flag
}

func wrapFlutterError(_ arg: Error) -> FlutterError {
return FlutterError(code: "\((arg as NSError).code)",
message: arg.localizedDescription,
details: nil)
}

// from NFCTagReaderSessionDelegate
public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
if tags.count > 1 {
Expand All @@ -392,9 +445,9 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
}
return
}

let firstTag = tags.first!

var result: [String: Any] = [:]
// default NDEF status
result["ndefAvailable"] = false
Expand All @@ -403,7 +456,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
// fake NDEF results
result["ndefType"] = ""
result["ndefCanMakeReadOnly"] = false

switch firstTag {
case let .iso7816(tag):
result["type"] = "iso7816"
Expand Down Expand Up @@ -449,15 +502,15 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
result["type"] = "unknown"
result["standard"] = "unknown"
}

session.connect(to: firstTag, completionHandler: { (error: Error?) in
if let error = error {
self.result?(FlutterError(code: "500", message: "Error connecting to card", details: error.localizedDescription))
self.result = nil
return
}
self.tag = firstTag

var ndefTag: NFCNDEFTag?
switch self.tag {
case let .iso7816(tag):
Expand All @@ -471,7 +524,7 @@ public class SwiftFlutterNfcKitPlugin: NSObject, FlutterPlugin, NFCTagReaderSess
default:
ndefTag = nil
}

if ndefTag != nil {
ndefTag!.queryNDEFStatus(completionHandler: { (status: NFCNDEFStatus, capacity: Int, error: Error?) in
if error == nil {
Expand Down
Loading
Loading