import { NfcPlugin, IsEnabledResult, TagDetectedEvent } from './definitions.js'; /** * Interface for native iOS app to implement for WebView communication */ interface NativeIosBridge { isNfcEnabled?: () => Promise; startNfcScan?: (options: string) => Promise; stopNfcScan?: () => Promise; writeNfcTag?: (data: string) => Promise; openSettings?: () => Promise; } /** * NFC implementation that communicates with a native iOS app via a bridge * Note: This requires custom implementation in a native iOS app */ export class IosBridgeNfc implements NfcPlugin { private bridge: NativeIosBridge; private listeners: { [key: string]: Array<(...args: any[]) => void> } = {}; private isBridgeAvailable: boolean = false; constructor() { // Look for the bridge in the global scope // The native app needs to inject this object this.bridge = (window as any).nativeNfcBridge || {}; this.isBridgeAvailable = !!(this.bridge.isNfcEnabled && this.bridge.startNfcScan); // Set up message listener for tag detection events from native app if (this.isBridgeAvailable) { window.addEventListener('message', this.handleNativeMessage.bind(this)); console.log('iOS NFC bridge initialized'); } else { console.warn('iOS NFC bridge not found'); } } private handleNativeMessage(event: MessageEvent) { if (!event.data || typeof event.data !== 'object') return; // Handle tag detected message from native app if (event.data.type === 'nfcTagDetected' && event.data.tag) { const tagListeners = this.listeners['tagDetected'] || []; const tag = event.data.tag as TagDetectedEvent; for (const listener of tagListeners) { listener(tag); } } // Handle NFC status change from native app if (event.data.type === 'nfcStatusChanged' && event.data.enabled !== undefined) { const statusListeners = this.listeners['nfcStatusChanged'] || []; for (const listener of statusListeners) { listener({ enabled: !!event.data.enabled }); } } } async isEnabled(): Promise { if (!this.isBridgeAvailable) { return { enabled: false }; } try { const enabled = await this.bridge.isNfcEnabled!(); return { enabled }; } catch (error) { console.error('Error checking NFC status through bridge:', error); return { enabled: false }; } } async openSettings(): Promise { if (!this.isBridgeAvailable || !this.bridge.openSettings) { throw new Error('NFC settings cannot be opened - bridge not available'); } try { await this.bridge.openSettings(); } catch (error) { console.error('Error opening NFC settings through bridge:', error); throw error; } } async startScanSession(options?: any): Promise { if (!this.isBridgeAvailable) { throw new Error('NFC scan cannot be started - bridge not available'); } try { await this.bridge.startNfcScan!(JSON.stringify(options || {})); } catch (error) { console.error('Error starting NFC scan through bridge:', error); throw error; } } async stopScanSession(): Promise { if (!this.isBridgeAvailable || !this.bridge.stopNfcScan) { return; // Just silently return if bridge not available } try { await this.bridge.stopNfcScan!(); } catch (error) { console.error('Error stopping NFC scan through bridge:', error); } } async write(options: any): Promise { if (!this.isBridgeAvailable || !this.bridge.writeNfcTag) { throw new Error('NFC write is not supported - bridge not available'); } try { await this.bridge.writeNfcTag!(JSON.stringify(options)); } catch (error) { console.error('Error writing NFC tag through bridge:', error); throw error; } } async makeReadOnly(): Promise { throw new Error('makeReadOnly is not implemented in the iOS bridge'); } async format(): Promise { throw new Error('format is not implemented in the iOS bridge'); } async erase(): Promise { throw new Error('erase is not implemented in the iOS bridge'); } async share(): Promise { throw new Error('share is not supported in iOS'); } async stopSharing(): Promise { throw new Error('share is not supported in iOS'); } async addListener( eventName: 'nfcStatusChanged' | 'tagDetected', listenerFunc: (data: any) => void, ): Promise { if (!this.listeners[eventName]) { this.listeners[eventName] = []; } this.listeners[eventName].push(listenerFunc); return { remove: async () => { this.removeListener(eventName, listenerFunc); }, }; } private removeListener(eventName: string, listenerFunc: (data: any) => void): void { if (this.listeners[eventName]) { this.listeners[eventName] = this.listeners[eventName].filter( listener => listener !== listenerFunc ); } } async removeAllListeners(): Promise { this.listeners = {}; } }