From 175faeb6643bf41acfdf8f5cfd11fa1b35625e99 Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Tue, 10 Oct 2023 10:04:44 -0300 Subject: [PATCH] add write fn --- plugins/nfc/guest-js/index.ts | 15 +++- plugins/nfc/ios/Sources/NfcPlugin.swift | 114 +++++++++++++++++++----- plugins/nfc/src/api-iife.js | 2 +- plugins/nfc/src/lib.rs | 13 ++- plugins/nfc/src/models.rs | 13 ++- 5 files changed, 131 insertions(+), 26 deletions(-) diff --git a/plugins/nfc/guest-js/index.ts b/plugins/nfc/guest-js/index.ts index 11b0fbc1..09a1cf80 100644 --- a/plugins/nfc/guest-js/index.ts +++ b/plugins/nfc/guest-js/index.ts @@ -14,7 +14,7 @@ export enum ScanKind { } export interface ScanOptions { - keepAlive?: boolean; + keepSessionAlive?: boolean; } export enum NFCTypeNameFormat { @@ -46,6 +46,13 @@ export interface Scan { tag: Tag; } +export interface NFCRecord { + format: NFCTypeNameFormat; + kind: number[]; + id: number[]; + payload: number[]; +} + export async function scan( kind: ScanKind, options?: ScanOptions, @@ -56,6 +63,12 @@ export async function scan( }); } +export async function write(records: NFCRecord[]): Promise { + return await window.__TAURI_INVOKE__("plugin:nfc|write", { + records, + }); +} + export async function isAvailable(): Promise { return await window.__TAURI_INVOKE__("plugin:nfc|isAvailable"); } diff --git a/plugins/nfc/ios/Sources/NfcPlugin.swift b/plugins/nfc/ios/Sources/NfcPlugin.swift index 8fa323e7..f50f8019 100644 --- a/plugins/nfc/ios/Sources/NfcPlugin.swift +++ b/plugins/nfc/ios/Sources/NfcPlugin.swift @@ -19,7 +19,7 @@ enum TagProcessMode { class Session { let nfcSession: NFCReaderSession? let invoke: Invoke - let keepAlive: Bool + var keepAlive: Bool let tagProcessMode: TagProcessMode var tagStatus: NFCNDEFStatus? var tag: NFCNDEFTag? @@ -51,9 +51,8 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega to: tag, completionHandler: { [self] (error) in if let error = error { - if self.session?.keepAlive != true { - self.closeSession(session, error: "cannot connect to tag: \(error)") - } + self.closeSession(session, error: "cannot connect to tag: \(error)") + } else { let ndefTag: NFCNDEFTag switch tag { @@ -101,9 +100,8 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega to: tag, completionHandler: { [self] (error) in if let error = error { - if self.session?.keepAlive != true { - self.closeSession(session, error: "cannot connect to tag: \(error)") - } + self.closeSession(session, error: "cannot connect to tag: \(error)") + } else { self.processTag( session: session, tag: tag, metadata: [:], @@ -171,9 +169,7 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega tag.queryNDEFStatus(completionHandler: { [self] (status, capacity, error) in if let error = error { - if self.session?.keepAlive != true { - self.closeSession(session, error: "cannot connect to tag: \(error)") - } + self.closeSession(session, error: "cannot connect to tag: \(error)") } else { switch mode { case .write: @@ -206,13 +202,14 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega currentSession.writeMessage!, completionHandler: { (error) in if let error = error { - if currentSession.keepAlive != true { - self.closeSession(session, error: "cannot write to tag: \(error)") - } + self.closeSession(session, error: "cannot write to tag: \(error)") } else { session.alertMessage = "Data wrote to NFC tag" currentSession.invoke.resolve() - self.closeSession(session) + + if currentSession.keepAlive != true { + self.closeSession(session) + } } }) } @@ -223,8 +220,26 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega } private func readNDEFTag( - session: NFCReaderSession, status: NFCNDEFStatus, tag: T, metadata: JsonObject + session: NFCReaderSession, status: NFCNDEFStatus, tag: T, metadata m: JsonObject ) { + var metadata: JsonObject = [:] + metadata.merge(m) { (_, new) in new } + + switch status { + case .notSupported: + self.resolveInvoke(message: nil, metadata: metadata) + self.closeSession(session) + return + case .readOnly: + metadata["readOnly"] = true + break + case .readWrite: + metadata["readOnly"] = false + break + default: + break + } + tag.readNDEF(completionHandler: { [self] (message, error) in if let error = error { @@ -237,7 +252,10 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega session.alertMessage = "Successfully read tag" self.resolveInvoke(message: message, metadata: metadata) - self.closeSession(session) + + if self.session?.keepAlive != true { + self.closeSession(session) + } }) } @@ -280,12 +298,62 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega return arr } + private func dataFromByteArray(_ array: [UInt8]) -> Data { + var data = Data(capacity: array.count) + + data.append(contentsOf: array) + + return data + } + @objc func isAvailable(_ invoke: Invoke) { invoke.resolve([ "available": NFCNDEFReaderSession.readingAvailable ]) } + @objc public func write(_ invoke: Invoke) { + guard let records = invoke.getArray("records", JSObject.self) else { + invoke.reject("`records` array is required") + return + } + + var ndefPayloads = [NFCNDEFPayload]() + + for record in records { + let format = record["format"] as? NSNumber ?? 0 + let type = record["kind"] as? [UInt8] ?? [] + let identifier = record["id"] as? [UInt8] ?? [] + let payload = record["payload"] as? [UInt8] ?? [] + + ndefPayloads.append( + NFCNDEFPayload( + format: NFCTypeNameFormat(rawValue: UInt8(truncating: format)) ?? .unknown, + type: dataFromByteArray(type), + identifier: dataFromByteArray(identifier), + payload: dataFromByteArray(payload) + ) + ) + } + + if let session = self.session { + session.writeMessage = NFCNDEFMessage(records: ndefPayloads) + if let nfcSession = session.nfcSession, let tagStatus = session.tagStatus, + let tag = session.tag + { + session.keepAlive = false + self.writeNDEFTag(session: nfcSession, status: tagStatus, tag: tag) + } else { + invoke.reject( + "connected tag not found, please wait for it to be available and then call write()") + } + } else { + self.startScanSession( + invoke: invoke, kind: .ndef, keepAlive: false, invalidateAfterFirstRead: false, + tagProcessMode: .write) + } + } + @objc public func scan(_ invoke: Invoke) { let kind: ScanKind switch invoke.getString("kind") { @@ -299,11 +367,15 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega invoke.reject("invalid `kind` argument, expected one of `tag`, `ndef`.") return } - self.startScanSession(invoke: invoke, kind: kind) + self.startScanSession( + invoke: invoke, kind: kind, keepAlive: invoke.getBool("keepSessionAlive", false), + invalidateAfterFirstRead: true, tagProcessMode: .read) } - private func startScanSession(invoke: Invoke, kind: ScanKind) { - let keepAlive = invoke.getBool("keepAlive", false) + private func startScanSession( + invoke: Invoke, kind: ScanKind, keepAlive: Bool, invalidateAfterFirstRead: Bool, + tagProcessMode: TagProcessMode + ) { let nfcSession: NFCReaderSession? switch kind { @@ -318,7 +390,7 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega nfcSession = NFCNDEFReaderSession( delegate: self, queue: DispatchQueue.main, - invalidateAfterFirstRead: true + invalidateAfterFirstRead: invalidateAfterFirstRead ) break } @@ -327,7 +399,7 @@ class NfcPlugin: Plugin, NFCTagReaderSessionDelegate, NFCNDEFReaderSessionDelega nfcSession?.begin() self.session = Session( - nfcSession: nfcSession, invoke: invoke, keepAlive: keepAlive, tagProcessMode: .read) + nfcSession: nfcSession, invoke: invoke, keepAlive: keepAlive, tagProcessMode: tagProcessMode) } } diff --git a/plugins/nfc/src/api-iife.js b/plugins/nfc/src/api-iife.js index 3e558e01..b9cd5873 100644 --- a/plugins/nfc/src/api-iife.js +++ b/plugins/nfc/src/api-iife.js @@ -1 +1 @@ -if("__TAURI__"in window){var __TAURI_NFC__=function(n){"use strict";var a,e;return n.ScanKind=void 0,(a=n.ScanKind||(n.ScanKind={}))[a.Ndef=0]="Ndef",a[a.Tag=1]="Tag",n.NFCTypeNameFormat=void 0,(e=n.NFCTypeNameFormat||(n.NFCTypeNameFormat={}))[e.Empty=0]="Empty",e[e.NfcWellKnown=1]="NfcWellKnown",e[e.Media=2]="Media",e[e.AbsoluteURI=3]="AbsoluteURI",e[e.NfcExternal=4]="NfcExternal",e[e.Unknown=5]="Unknown",e[e.Unchanged=6]="Unchanged",n.isAvailable=async function(){return await window.__TAURI_INVOKE__("plugin:nfc|isAvailable")},n.scan=async function(a,e){return await window.__TAURI_INVOKE__("plugin:nfc|scan",{kind:a===n.ScanKind.Ndef?"ndef":"tag",...e})},n}({});Object.defineProperty(window.__TAURI__,"nfc",{value:__TAURI_NFC__})} +if("__TAURI__"in window){var __TAURI_NFC__=function(n){"use strict";var a,e;return n.ScanKind=void 0,(a=n.ScanKind||(n.ScanKind={}))[a.Ndef=0]="Ndef",a[a.Tag=1]="Tag",n.NFCTypeNameFormat=void 0,(e=n.NFCTypeNameFormat||(n.NFCTypeNameFormat={}))[e.Empty=0]="Empty",e[e.NfcWellKnown=1]="NfcWellKnown",e[e.Media=2]="Media",e[e.AbsoluteURI=3]="AbsoluteURI",e[e.NfcExternal=4]="NfcExternal",e[e.Unknown=5]="Unknown",e[e.Unchanged=6]="Unchanged",n.isAvailable=async function(){return await window.__TAURI_INVOKE__("plugin:nfc|isAvailable")},n.scan=async function(a,e){return await window.__TAURI_INVOKE__("plugin:nfc|scan",{kind:a===n.ScanKind.Ndef?"ndef":"tag",...e})},n.write=async function(n){return await window.__TAURI_INVOKE__("plugin:nfc|write",{records:n})},n}({});Object.defineProperty(window.__TAURI__,"nfc",{value:__TAURI_NFC__})} diff --git a/plugins/nfc/src/lib.rs b/plugins/nfc/src/lib.rs index b3d9da88..3c97e544 100644 --- a/plugins/nfc/src/lib.rs +++ b/plugins/nfc/src/lib.rs @@ -4,7 +4,7 @@ #![cfg(mobile)] -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use tauri::{ plugin::{Builder, PluginHandle, TauriPlugin}, Manager, Runtime, @@ -32,6 +32,11 @@ struct IsAvailableResponse { available: bool, } +#[derive(Serialize)] +struct WriteRequest { + records: Vec, +} + impl Nfc { pub fn is_available(&self) -> crate::Result { self.0 @@ -45,6 +50,12 @@ impl Nfc { .run_mobile_plugin("scan", payload) .map_err(Into::into) } + + pub fn write(&self, records: Vec) -> crate::Result<()> { + self.0 + .run_mobile_plugin("write", WriteRequest { records }) + .map_err(Into::into) + } } /// Extensions to [`tauri::App`], [`tauri::AppHandle`] and [`tauri::Window`] to access the nfc APIs. diff --git a/plugins/nfc/src/models.rs b/plugins/nfc/src/models.rs index ada1a34a..d0a6680d 100644 --- a/plugins/nfc/src/models.rs +++ b/plugins/nfc/src/models.rs @@ -9,10 +9,19 @@ use std::fmt::Display; #[serde(rename_all = "camelCase")] pub struct ScanRequest { pub kind: ScanKind, - pub keep_alive: bool, + pub keep_session_alive: bool, } -#[derive(serde_repr::Deserialize_repr)] +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NfcRecord { + pub format: NFCTypeNameFormat, + pub kind: Vec, + pub id: Vec, + pub payload: Vec, +} + +#[derive(serde_repr::Deserialize_repr, serde_repr::Serialize_repr)] #[repr(u8)] pub enum NFCTypeNameFormat { Empty = 0,