unfc
Version:
A framework-agnostic NFC package for reading/writing NFC tags in web apps and PWAs
136 lines (135 loc) • 4.91 kB
JavaScript
/**
* 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 {
constructor() {
this.listeners = {};
this.isBridgeAvailable = false;
// Look for the bridge in the global scope
// The native app needs to inject this object
this.bridge = window.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');
}
}
handleNativeMessage(event) {
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;
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() {
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() {
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) {
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() {
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) {
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() {
throw new Error('makeReadOnly is not implemented in the iOS bridge');
}
async format() {
throw new Error('format is not implemented in the iOS bridge');
}
async erase() {
throw new Error('erase is not implemented in the iOS bridge');
}
async share() {
throw new Error('share is not supported in iOS');
}
async stopSharing() {
throw new Error('share is not supported in iOS');
}
async addListener(eventName, listenerFunc) {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName].push(listenerFunc);
return {
remove: async () => {
this.removeListener(eventName, listenerFunc);
},
};
}
removeListener(eventName, listenerFunc) {
if (this.listeners[eventName]) {
this.listeners[eventName] = this.listeners[eventName].filter(listener => listener !== listenerFunc);
}
}
async removeAllListeners() {
this.listeners = {};
}
}