added ios support
This commit is contained in:
parent
0ff7c427e0
commit
37fc6f8b57
273
README.md
273
README.md
@ -1,6 +1,6 @@
|
||||
# Universal NFC
|
||||
|
||||
A framework-agnostic NFC package for reading and writing NFC tags in web applications and Progressive Web Apps (PWAs). This library provides a consistent API for working with NFC across different environments, with a focus on ease of use and developer experience.
|
||||
A framework-agnostic NFC package for reading and writing NFC tags in web applications and Progressive Web Apps (PWAs), with iOS support options. This library provides a consistent API for working with NFC across different environments, with a focus on ease of use and developer experience.
|
||||
|
||||
[](https://www.npmjs.com/package/universal-nfc)
|
||||
[](https://github.com/yourusername/universal-nfc/blob/main/LICENSE)
|
||||
@ -14,6 +14,7 @@ A framework-agnostic NFC package for reading and writing NFC tags in web applica
|
||||
- 📊 TypeScript support with comprehensive type definitions
|
||||
- 🔄 Simple and advanced APIs for different use cases
|
||||
- 🔌 Based on the Web NFC API standard
|
||||
- 🍎 iOS support via native bridge integration
|
||||
|
||||
## Installation
|
||||
|
||||
@ -29,10 +30,15 @@ yarn add universal-nfc
|
||||
|
||||
## Platform Compatibility
|
||||
|
||||
| Feature | Chrome for Android (89+) | Chrome for Desktop | Safari iOS | Firefox | Edge |
|
||||
| ----------- | ------------------------ | ------------------ | ---------- | ------- | ---- |
|
||||
| Reading NFC | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Writing NFC | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Feature | Chrome for Android (89+) | Chrome for Desktop | Safari iOS | Native iOS Apps | Native Android Apps | Firefox | Edge |
|
||||
|--------------|------------------------|------------------|-----------|----------------|----------------|---------|------|
|
||||
| Reading NFC | ✅ | ❌ | ❌ | ✅* | ✅ | ❌ | ❌ |
|
||||
| Writing NFC | ✅ | ❌ | ❌ | ✅* | ✅ | ❌ | ❌ |
|
||||
|
||||
\*iOS support requires a native app with an embedded WebView and bridge implementation.
|
||||
|
||||
|
||||
|
||||
|
||||
**Requirements for Web NFC:**
|
||||
|
||||
@ -41,6 +47,15 @@ yarn add universal-nfc
|
||||
- Device with NFC hardware
|
||||
- NFC enabled in device settings
|
||||
|
||||
|
||||
|
||||
**Requirements for iOS NFC:**
|
||||
|
||||
- iOS 11+ device with NFC hardware
|
||||
- Native iOS app with NFC entitlements
|
||||
- Core NFC framework integration
|
||||
- Custom bridge implementation (see iOS Integration section)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Reading NFC Tags (Simple API)
|
||||
@ -48,47 +63,45 @@ yarn add universal-nfc
|
||||
The simplest way to read NFC tags:
|
||||
|
||||
```javascript
|
||||
import { SimpleNfc } from "universal-nfc";
|
||||
import { SimpleNfc } from 'universal-nfc';
|
||||
|
||||
const nfcReader = new SimpleNfc();
|
||||
|
||||
async function startReading() {
|
||||
// First check if NFC is available
|
||||
const available = await nfcReader.isAvailable();
|
||||
// First check if NFC is available
|
||||
const available = await nfcReader.isAvailable();
|
||||
|
||||
if (!available) {
|
||||
console.log("NFC is not available on this device/browser");
|
||||
return;
|
||||
}
|
||||
if (!available) {
|
||||
console.log('NFC is not available on this device/browser');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Start reading NFC tags with a callback
|
||||
await nfcReader.startReading((content, type) => {
|
||||
console.log(`Read ${type} content:`, content);
|
||||
try {
|
||||
// Start reading NFC tags with a callback
|
||||
await nfcReader.startReading((content, type) => {
|
||||
console.log(`Read ${type} content:`, content);
|
||||
|
||||
// 'type' will be 'text', 'url', or 'other'
|
||||
if (type === "url") {
|
||||
// Handle URL
|
||||
window.open(content, "_blank");
|
||||
} else {
|
||||
// Handle text or other content
|
||||
document.getElementById("result").textContent = content;
|
||||
}
|
||||
});
|
||||
// 'type' will be 'text', 'url', or 'other'
|
||||
if (type === 'url') {
|
||||
// Handle URL
|
||||
window.open(content, '_blank');
|
||||
} else {
|
||||
// Handle text or other content
|
||||
document.getElementById('result').textContent = content;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Scan started - tap an NFC tag");
|
||||
} catch (error) {
|
||||
console.error("Error starting NFC scan:", error);
|
||||
}
|
||||
console.log('Scan started - tap an NFC tag');
|
||||
} catch (error) {
|
||||
console.error('Error starting NFC scan:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function stopReading() {
|
||||
nfcReader.stopReading();
|
||||
console.log("NFC reading stopped");
|
||||
nfcReader.stopReading();
|
||||
console.log('NFC reading stopped');
|
||||
}
|
||||
|
||||
// Call startReading() when your app is ready to scan
|
||||
// Call stopReading() when you want to stop scanning
|
||||
```
|
||||
|
||||
### Writing to NFC Tags
|
||||
@ -117,6 +130,196 @@ async function writeUrlToTag() {
|
||||
}
|
||||
```
|
||||
|
||||
## iOS Integration
|
||||
|
||||
### Overview
|
||||
|
||||
iOS devices with NFC hardware (iPhone 7 and later running iOS 11+) support reading NFC tags, but with some important restrictions:
|
||||
|
||||
- **Safari browser cannot directly access NFC**: The Web NFC API is not supported in Safari.
|
||||
- **Native app required**: NFC on iOS requires a native app built with the Core NFC framework.
|
||||
- **Web apps can access NFC through a bridge**: Web content running in a WebView inside a native app can access NFC via a custom bridge.
|
||||
|
||||
### iOS Native Bridge Setup
|
||||
|
||||
To use Universal NFC with iOS, you need to:
|
||||
|
||||
1. Create a native iOS app with WebView.
|
||||
2. Implement Core NFC.
|
||||
3. Create a JavaScript bridge.
|
||||
|
||||
#### 1. Native iOS App with NFC Capabilities
|
||||
|
||||
First, ensure your app has NFC entitlements:
|
||||
|
||||
- Add the NFC entitlement to your app in Xcode.
|
||||
- Add `NFCReaderUsageDescription` in `Info.plist`.
|
||||
- Enable the "Near Field Communication Tag Reading" capability.
|
||||
|
||||
#### 2. Implement the NFC Bridge in Swift
|
||||
|
||||
```swift
|
||||
import WebKit
|
||||
import CoreNFC
|
||||
|
||||
class NfcBridge: NSObject, NFCNDEFReaderSessionDelegate {
|
||||
weak var webView: WKWebView?
|
||||
var nfcSession: NFCNDEFReaderSession?
|
||||
|
||||
init(webView: WKWebView) {
|
||||
self.webView = webView
|
||||
super.init()
|
||||
|
||||
// Register JavaScript interface
|
||||
let bridgeScript = WKUserScript(
|
||||
source: "window.nativeNfcBridge = {
|
||||
isNfcEnabled: function() { return window.webkit.messageHandlers.nfcBridge.postMessage({action: 'isEnabled'}); },
|
||||
startNfcScan: function(options) { return window.webkit.messageHandlers.nfcBridge.postMessage({action: 'startScan', options: options}); },
|
||||
stopNfcScan: function() { return window.webkit.messageHandlers.nfcBridge.postMessage({action: 'stopScan'}); },
|
||||
writeNfcTag: function(data) { return window.webkit.messageHandlers.nfcBridge.postMessage({action: 'writeTag', data: data}); },
|
||||
openSettings: function() { return window.webkit.messageHandlers.nfcBridge.postMessage({action: 'openSettings'}); }
|
||||
};",
|
||||
injectionTime: .atDocumentStart,
|
||||
forMainFrameOnly: false
|
||||
)
|
||||
|
||||
let contentController = webView.configuration.userContentController
|
||||
contentController.addUserScript(bridgeScript)
|
||||
contentController.add(self, name: "nfcBridge")
|
||||
}
|
||||
|
||||
// Handle JavaScript messages
|
||||
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||
guard let body = message.body as? [String: Any],
|
||||
let action = body["action"] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
switch action {
|
||||
case "isEnabled":
|
||||
checkNfcAvailability()
|
||||
case "startScan":
|
||||
let options = body["options"] as? String ?? "{}"
|
||||
startNfcSession(options: options)
|
||||
case "stopScan":
|
||||
stopNfcSession()
|
||||
case "writeTag":
|
||||
if let data = body["data"] as? String {
|
||||
writeNfcTag(data: data)
|
||||
}
|
||||
case "openSettings":
|
||||
openSettings()
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func checkNfcAvailability() {
|
||||
let isAvailable = NFCNDEFReaderSession.readingAvailable
|
||||
sendToWebView(script: "window.dispatchEvent(new MessageEvent('message', {data: {type: 'nfcStatusChanged', enabled: \(isAvailable)}}));")
|
||||
}
|
||||
|
||||
private func startNfcSession(options: String) {
|
||||
guard NFCNDEFReaderSession.readingAvailable else {
|
||||
sendToWebView(script: "console.error('NFC reading not available on this device');")
|
||||
return
|
||||
}
|
||||
|
||||
nfcSession = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
|
||||
nfcSession?.alertMessage = "Hold your iPhone near an NFC tag"
|
||||
nfcSession?.begin()
|
||||
}
|
||||
|
||||
private func stopNfcSession() {
|
||||
nfcSession?.invalidate()
|
||||
nfcSession = nil
|
||||
}
|
||||
|
||||
private func writeNfcTag(data: String) {
|
||||
// Implement write logic
|
||||
}
|
||||
|
||||
private func openSettings() {
|
||||
if let url = URL(string: UIApplication.openSettingsURLString) {
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func sendToWebView(script: String) {
|
||||
DispatchQueue.main.async {
|
||||
self.webView?.evaluateJavaScript(script, completionHandler: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Initialize the Bridge in Your View Controller
|
||||
|
||||
```swift
|
||||
import UIKit
|
||||
import WebKit
|
||||
|
||||
class WebViewController: UIViewController {
|
||||
var webView: WKWebView!
|
||||
var nfcBridge: NfcBridge!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
webView = WKWebView(frame: view.bounds)
|
||||
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
view.addSubview(webView)
|
||||
|
||||
nfcBridge = NfcBridge(webView: webView)
|
||||
|
||||
if let url = URL(string: "https://your-web-app-url.com") {
|
||||
webView.load(URLRequest(url: url))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using the iOS Bridge in Your Web App
|
||||
|
||||
The native bridge will automatically be detected by Universal NFC when your web app runs inside the native iOS app's WebView:
|
||||
|
||||
```javascript
|
||||
import { Nfc } from 'universal-nfc';
|
||||
|
||||
const nfc = new Nfc();
|
||||
|
||||
async function checkNfcSupport() {
|
||||
const { enabled } = await nfc.isEnabled();
|
||||
console.log('NFC supported and enabled:', enabled);
|
||||
|
||||
if (enabled) {
|
||||
await nfc.startScanSession();
|
||||
|
||||
await nfc.addListener('tagDetected', (tag) => {
|
||||
console.log('NFC tag detected via iOS bridge:', tag);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Troubleshooting iOS-Specific Issues
|
||||
|
||||
#### "NFC reading is not supported in web browsers on iOS"
|
||||
- This error occurs when trying to use NFC in Safari or a standard WebView.
|
||||
- **Solution**: Use the native bridge approach with a custom iOS app.
|
||||
|
||||
#### "CoreNFC framework missing"
|
||||
- Make sure you have the proper entitlements in your iOS app.
|
||||
- Check that your iOS device supports NFC (iPhone 7 and later).
|
||||
|
||||
#### "Bridge communication error"
|
||||
- Check the bridge implementation in your native app.
|
||||
- Verify message format passed between WebView and native code.
|
||||
|
||||
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core API (Nfc class)
|
||||
@ -438,7 +641,3 @@ Permissions-Policy: nfc=self
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Credits
|
||||
|
||||
This library is inspired by the API design of [@capawesome-team/capacitor-nfc](https://github.com/capawesome-team/capacitor-plugins/tree/main/packages/nfc), but implements a framework-agnostic solution based on the Web NFC API.
|
||||
|
174
ios-bridge.ts
Normal file
174
ios-bridge.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import { NfcPlugin, IsEnabledResult, TagDetectedEvent } from './definitions.js';
|
||||
|
||||
/**
|
||||
* Interface for native iOS app to implement for WebView communication
|
||||
*/
|
||||
interface NativeIosBridge {
|
||||
isNfcEnabled?: () => Promise<boolean>;
|
||||
startNfcScan?: (options: string) => Promise<void>;
|
||||
stopNfcScan?: () => Promise<void>;
|
||||
writeNfcTag?: (data: string) => Promise<void>;
|
||||
openSettings?: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<IsEnabledResult> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
throw new Error('makeReadOnly is not implemented in the iOS bridge');
|
||||
}
|
||||
|
||||
async format(): Promise<void> {
|
||||
throw new Error('format is not implemented in the iOS bridge');
|
||||
}
|
||||
|
||||
async erase(): Promise<void> {
|
||||
throw new Error('erase is not implemented in the iOS bridge');
|
||||
}
|
||||
|
||||
async share(): Promise<void> {
|
||||
throw new Error('share is not supported in iOS');
|
||||
}
|
||||
|
||||
async stopSharing(): Promise<void> {
|
||||
throw new Error('share is not supported in iOS');
|
||||
}
|
||||
|
||||
async addListener(
|
||||
eventName: 'nfcStatusChanged' | 'tagDetected',
|
||||
listenerFunc: (data: any) => void,
|
||||
): Promise<any> {
|
||||
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<void> {
|
||||
this.listeners = {};
|
||||
}
|
||||
}
|
73
ios-detection.ts
Normal file
73
ios-detection.ts
Normal file
@ -0,0 +1,73 @@
|
||||
// src/ios-detection.ts
|
||||
export interface IosSupportInfo {
|
||||
isIos: boolean;
|
||||
version: number | null;
|
||||
supportsNfc: boolean;
|
||||
requiresNative: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides detailed information about iOS NFC compatibility
|
||||
*/
|
||||
export class IosDetection {
|
||||
/**
|
||||
* Detects if the current device is running iOS and its NFC capabilities
|
||||
*/
|
||||
static getIosSupportInfo(): IosSupportInfo {
|
||||
const userAgent = navigator.userAgent;
|
||||
|
||||
// Detect iOS
|
||||
const isIos = /iphone|ipad|ipod/i.test(userAgent);
|
||||
if (!isIos) {
|
||||
return { isIos: false, version: null, supportsNfc: false, requiresNative: false };
|
||||
}
|
||||
|
||||
// Extract iOS version
|
||||
const versionMatch = userAgent.match(/OS (\d+)_(\d+)_?(\d+)?/);
|
||||
const version = versionMatch ?
|
||||
parseInt(versionMatch[1], 10) + (parseInt(versionMatch[2], 10) / 10) :
|
||||
null;
|
||||
|
||||
// iOS NFC support info:
|
||||
// - iOS 11+ supports NFC reading but requires a native app
|
||||
// - No iOS version supports Web NFC API
|
||||
const supportsNfc = version !== null && version >= 11;
|
||||
|
||||
return {
|
||||
isIos,
|
||||
version,
|
||||
supportsNfc,
|
||||
requiresNative: true // iOS always requires native app for NFC
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this device supports NFC in any form (native or web)
|
||||
*/
|
||||
static hasNfcHardware(): boolean {
|
||||
const iosInfo = this.getIosSupportInfo();
|
||||
if (iosInfo.isIos) {
|
||||
return iosInfo.supportsNfc;
|
||||
}
|
||||
|
||||
// For Android/other platforms, we check based on the user agent
|
||||
return /android/i.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides guidance for enabling NFC on iOS
|
||||
*/
|
||||
static getIosNfcGuidance(): string {
|
||||
const iosInfo = this.getIosSupportInfo();
|
||||
|
||||
if (!iosInfo.isIos) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!iosInfo.supportsNfc) {
|
||||
return 'Your iOS device does not support NFC or is running an iOS version below 11.0.';
|
||||
}
|
||||
|
||||
return 'NFC on iOS requires a native app. The web browser cannot directly access NFC hardware on iOS devices.';
|
||||
}
|
||||
}
|
15
nfc.ts
15
nfc.ts
@ -10,6 +10,8 @@ import {
|
||||
NfcErrorType,
|
||||
} from "./definitions.js";
|
||||
import { WebNfc } from "./web.js";
|
||||
import { IosBridgeNfc } from './ios-bridge.js';
|
||||
import { IosDetection } from './ios-detection.js';
|
||||
|
||||
/**
|
||||
* Main NFC class that provides access to NFC functionality.
|
||||
@ -18,15 +20,24 @@ import { WebNfc } from "./web.js";
|
||||
export class Nfc {
|
||||
private implementation: NfcPlugin;
|
||||
private listeners: Map<string, Set<Function>> = new Map();
|
||||
private iosInfo: any;
|
||||
|
||||
|
||||
constructor() {
|
||||
// Currently we only have the Web implementation
|
||||
this.implementation = new WebNfc();
|
||||
this.iosInfo = IosDetection.getIosSupportInfo();
|
||||
|
||||
if (this.iosInfo.isIos && typeof (window as any).nativeNfcBridge !== 'undefined') {
|
||||
console.log('Using iOS Native Bridge for NFC');
|
||||
this.implementation = new IosBridgeNfc();
|
||||
} else {
|
||||
this.implementation = new WebNfc();
|
||||
}
|
||||
|
||||
// Set up status monitoring to track NFC availability
|
||||
this.monitorNfcStatus();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal method to monitor NFC status changes
|
||||
*/
|
||||
|
105
web.ts
105
web.ts
@ -6,8 +6,10 @@ import {
|
||||
ShareOptions,
|
||||
PluginListenerHandle,
|
||||
TagDetectedEvent,
|
||||
NfcErrorType,
|
||||
NdefRecord,
|
||||
} from "./definitions.js";
|
||||
} from './definitions.js';
|
||||
import { IosDetection } from './ios-detection.js';
|
||||
|
||||
export class WebNfc implements NfcPlugin {
|
||||
private scanSessionActive = false;
|
||||
@ -15,73 +17,104 @@ export class WebNfc implements NfcPlugin {
|
||||
private listeners: { [key: string]: Array<(...args: any[]) => void> } = {};
|
||||
private ndefReader: any = null;
|
||||
private nfcSupported: boolean = false;
|
||||
private iosInfo: any = null;
|
||||
|
||||
constructor() {
|
||||
this.detectNfcSupport();
|
||||
}
|
||||
|
||||
|
||||
private detectNfcSupport() {
|
||||
// Check if Web NFC API is available
|
||||
if (typeof window !== "undefined") {
|
||||
// Primary check for NDEFReader
|
||||
this.nfcSupported = "NDEFReader" in window;
|
||||
// Check for iOS first
|
||||
this.iosInfo = IosDetection.getIosSupportInfo();
|
||||
|
||||
if (this.iosInfo.isIos) {
|
||||
this.nfcSupported = false;
|
||||
console.info('iOS device detected:', this.iosInfo);
|
||||
|
||||
if (this.iosInfo.supportsNfc) {
|
||||
console.warn('iOS NFC requires a native app wrapper - web browser access is not supported');
|
||||
} else {
|
||||
console.warn('NFC is not supported on this iOS device or iOS version');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check Web NFC API for other platforms (primarily Android)
|
||||
if (typeof window !== 'undefined') {
|
||||
this.nfcSupported = 'NDEFReader' in window;
|
||||
|
||||
// Log platform info for debugging
|
||||
const userAgent = navigator.userAgent;
|
||||
const isHttps = window.location.protocol === "https:";
|
||||
const isPWA = window.matchMedia("(display-mode: standalone)").matches;
|
||||
const isHttps = window.location.protocol === 'https:';
|
||||
const isPWA = window.matchMedia('(display-mode: standalone)').matches;
|
||||
|
||||
console.info("NFC Support Check:", {
|
||||
console.info('NFC Support Check:', {
|
||||
supported: this.nfcSupported,
|
||||
isHttps: isHttps,
|
||||
isPWA: isPWA,
|
||||
isAndroid: /android/i.test(userAgent),
|
||||
isIOS: /iphone|ipad|ipod/i.test(userAgent),
|
||||
isChrome: /chrome/i.test(userAgent),
|
||||
isChrome: /chrome/i.test(userAgent)
|
||||
});
|
||||
|
||||
if (!this.nfcSupported) {
|
||||
// Provide helpful message about browser compatibility
|
||||
const reason = !isHttps
|
||||
? "NFC requires HTTPS"
|
||||
: !/android/i.test(userAgent)
|
||||
? "NFC Web API only supported on Android"
|
||||
: !/chrome/i.test(userAgent)
|
||||
? "NFC Web API only supported in Chrome-based browsers"
|
||||
: "This browser does not support the Web NFC API";
|
||||
const reason = !isHttps ? 'NFC requires HTTPS' :
|
||||
!/android/i.test(userAgent) ? 'NFC Web API only supported on Android' :
|
||||
!/chrome/i.test(userAgent) ? 'NFC Web API only supported in Chrome-based browsers' :
|
||||
'This browser does not support the Web NFC API';
|
||||
|
||||
console.warn(`Web NFC not available: ${reason}`);
|
||||
}
|
||||
} else {
|
||||
this.nfcSupported = false;
|
||||
console.warn("Web NFC not available: Not in browser environment");
|
||||
console.warn('Web NFC not available: Not in browser environment');
|
||||
}
|
||||
}
|
||||
|
||||
async isEnabled(): Promise<IsEnabledResult> {
|
||||
// For iOS, provide accurate info without throwing errors
|
||||
if (this.iosInfo?.isIos) {
|
||||
return { enabled: false };
|
||||
}
|
||||
|
||||
return { enabled: this.nfcSupported };
|
||||
}
|
||||
|
||||
async openSettings(): Promise<void> {
|
||||
// Special handling for iOS
|
||||
if (this.iosInfo?.isIos) {
|
||||
const message = IosDetection.getIosNfcGuidance();
|
||||
|
||||
// Use alert() on iOS to show guidance
|
||||
if (typeof alert === 'function') {
|
||||
alert(message);
|
||||
}
|
||||
|
||||
console.warn('iOS NFC guidance:', message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.nfcSupported) {
|
||||
throw this.createCompatibilityError();
|
||||
}
|
||||
|
||||
console.warn(
|
||||
"openSettings: On web, users must enable NFC in device settings manually."
|
||||
);
|
||||
console.warn('openSettings: On web, users must enable NFC in device settings manually.');
|
||||
|
||||
// Provide instructions based on browser detection
|
||||
if (/android/i.test(navigator.userAgent)) {
|
||||
alert(
|
||||
"Please enable NFC in your device settings: Settings > Connected devices > Connection preferences > NFC"
|
||||
);
|
||||
alert('Please enable NFC in your device settings: Settings > Connected devices > Connection preferences > NFC');
|
||||
} else {
|
||||
alert("Please ensure NFC is enabled on your device.");
|
||||
alert('Please ensure NFC is enabled on your device.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async startScanSession(options?: StartScanSessionOptions): Promise<void> {
|
||||
|
||||
if (this.iosInfo?.isIos) {
|
||||
throw new Error("NFC reading is not supported in web browsers on iOS. " +
|
||||
"To use NFC on iOS, you need a native app implementation.");
|
||||
}
|
||||
|
||||
if (!this.nfcSupported) {
|
||||
throw this.createCompatibilityError();
|
||||
}
|
||||
@ -364,18 +397,10 @@ export class WebNfc implements NfcPlugin {
|
||||
}
|
||||
|
||||
private createCompatibilityError(): Error {
|
||||
return new Error(
|
||||
"Web NFC API is not supported in this browser. NFC Web API requires Chrome 89+ on Android with NFC hardware, running over HTTPS."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
import { NFCDefinition } from "./definitions";
|
||||
|
||||
export class WebNFC {
|
||||
// Implement your web NFC functionalities here
|
||||
read(): NFCDefinition {
|
||||
// Dummy implementation
|
||||
return { id: "1", data: "sample data" };
|
||||
if (this.iosInfo?.isIos) {
|
||||
return new Error("iOS browsers don't support the Web NFC API. To use NFC on iOS, you need a native app implementation.");
|
||||
}
|
||||
|
||||
return new Error('Web NFC API is not supported in this browser. NFC Web API requires Chrome 89+ on Android with NFC hardware, running over HTTPS.');
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user