Créer un APK gratuit

API du pont JavaScript

NFC

Lecture d'un tag via window.AndroidNFC, et le côté écriture derrière le polyfill NDEFReader.

NDEFReader standard web#

new NDEFReader() — Web NFC

Exemple

Web NFC standard : new NDEFReader() lit, écrit et verrouille des tags NFC dans l'application installée, avec le même code que celui accepté par Chrome sur Android.

Retourne : des Promises et des événements reading / readingerror, comme dans le standard web. Les enregistrements sont des NDEFRecord : recordType ('text', 'url', 'absolute-url', 'mime', 'smart-poster', 'empty', 'unknown' ou un type externe tel que 'example.com:thing'), mediaType (enregistrements mime), id, encoding et lang (enregistrements texte) et data sous forme de DataView. Nécessite : activez Web NFC Support à l'étape 4 (Accès) lors de la compilation. Sans cela, NDEFReader n'existe pas. Aucune demande d'autorisation à l'exécution ; navigator.permissions.query({name:'nfc'}) répond granted.

Lire des tags. Décodez data avec TextDecoder, exactement comme dans Chrome :

function describe(record) {
  if (record.recordType === 'text') return new TextDecoder(record.encoding).decode(record.data);
  if (record.recordType === 'url' || record.recordType === 'absolute-url') return new TextDecoder().decode(record.data);
  if (record.recordType === 'mime') return record.mediaType + ' (' + record.data.byteLength + ' bytes)';
  return record.recordType;
}

var scanStop = null;

document.getElementById('read').addEventListener('click', async function () {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  var reader = new NDEFReader();
  scanStop = new AbortController();
  reader.onreading = function (e) {
    showStatus('Tag ' + e.serialNumber + ': ' + e.message.records.map(describe).join(' | '));
  };
  reader.onreadingerror = function () { showStatus('That tag holds no NFC data this phone can read.'); };
  try {
    await reader.scan({ signal: scanStop.signal });
    showStatus('Hold a tag to the back of the phone.');
  } catch (err) {
    showStatus('NFC error: ' + err.name + ' ' + err.message);   // NotReadableError = NFC switched off
  }
});

document.getElementById('stop').addEventListener('click', function () {
  if (scanStop) scanStop.abort();   // the standard way to stop scanning
});

Écrire du texte ou un lien. La Promise se résout après que l'utilisateur a passé un tag :

async function writeTag(kind, value) {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  var reader = new NDEFReader();
  showStatus('Tap a tag to write it...');
  try {
    if (kind === 'text') await reader.write(value);   // same as { records: [{ recordType: 'text', data: value }] }
    else await reader.write({ records: [{ recordType: 'url', data: value }] }, { overwrite: false });
    showStatus('Tag written.');
  } catch (err) {
    showStatus('Write failed: ' + err.name + ' ' + err.message);   // NotAllowedError: read-only, or overwrite:false on a used tag
  }
}

Verrouiller un tag pour toujours. makeReadOnly() est irréversible - demandez d'abord :

async function lockTag() {
  if (!('NDEFReader' in window)) return;
  if (!confirm('Lock the next tag you tap? It can never be changed again.')) return;
  try { await new NDEFReader().makeReadOnly(); showStatus('Tag locked.'); }
  catch (err) { showStatus('Lock failed: ' + err.message); }
}

Remarques : Types d'enregistrement pour write : text (chaîne, ou octets avec encoding), url et absolute-url (chaîne), mime (octets + mediaType), smart-poster ({records: [...]}), un type externe 'domain:thing' (octets ou {records: [...]}), unknown (octets) et empty. Une simple chaîne écrit un enregistrement texte ; de simples octets écrivent un enregistrement mime application/octet-stream. Seuls les lecteurs ayant appelé scan() reçoivent les événements reading ; AbortController.abort() sur le signal arrête ce lecteur (il n'y a pas de stop() dans le standard). readingerror se déclenche pour un tag qui ne peut pas contenir de données NFC. Le téléphone n'attend qu'UNE opération de tag à la fois : un nouveau write() ou makeReadOnly() pendant qu'une attend la remplace, et l'appel remplacé est rejeté avec AbortError (Chrome le mettrait en file). Un signal sur write() / makeReadOnly() annule l'attente. Erreurs : NotSupportedError (pas de NFC, ou le tag ne peut pas contenir de NDEF, ou il est trop petit), NotReadableError (NFC désactivé), NotAllowedError (tag en lecture seule, ou overwrite: false sur un tag contenant des enregistrements), NetworkError (le tag s'est éloigné), TypeError (un enregistrement non autorisé par le standard). NDEFReader existe dans la page principale, pas dans les iframes.

__nfcCancelWrite pont#

window.WebToApk.__nfcCancelWrite()

Annule l'écriture/le verrouillage en attente (le polyfill l'appelle quand le `signal` de l'opération est interrompu).

Exemple

Supprime une écriture ou un verrouillage NFC en file pour que le prochain passage de tag soit lu normalement. Les pages annulent via le standard web - un AbortController signal - et le polyfill NDEFReader appelle ceci à leur place.

Retourne : rien. Nécessite : Web NFC Support activé à l'étape 4 (Accès) lors de la compilation.

La méthode standard, que l'application respecte - le write() en attente est rejeté avec AbortError:

var controller = null;

async function writeWithCancel(text) {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  controller = new AbortController();
  showStatus('Tap a tag to write it... (or press Cancel)');
  try {
    await new NDEFReader().write(text, { signal: controller.signal });
    showStatus('Tag written.');
  } catch (err) {
    showStatus(err.name === 'AbortError' ? 'Cancelled.' : 'Write failed: ' + err.message);
  }
}

document.getElementById('cancel').addEventListener('click', function () {
  if (controller) controller.abort();
});

Abandonne tout seul quand aucun tag n'arrive à temps ; un minuteur qui interrompt le signal annule de la même façon :

async function writeWithTimeout(url, ms) {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  var controller = new AbortController();
  var timer = setTimeout(function () { controller.abort(); }, ms);
  showStatus('Tap a tag within ' + Math.round(ms / 1000) + ' seconds...');
  try {
    await new NDEFReader().write({ records: [{ recordType: 'url', data: url }] }, { signal: controller.signal });
    showStatus('Tag written.');
  } catch (err) {
    showStatus(err.name === 'AbortError' ? 'Timed out. Tap Write to try again.' : 'Write failed: ' + err.message);
  } finally {
    clearTimeout(timer);
  }
}

Annulez un verrouillage (ou une écriture) en attente quand l'utilisateur quitte l'écran, pour qu'un passage ultérieur ne modifie pas un tag :

var pendingTagOp = null;

window.addEventListener('hashchange', function () {
  if (pendingTagOp) { pendingTagOp.abort(); pendingTagOp = null; }
});

document.getElementById('lock').addEventListener('click', function () {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  if (pendingTagOp) pendingTagOp.abort();
  var controller = pendingTagOp = new AbortController();
  showStatus('Tap the tag to lock it...');
  new NDEFReader().makeReadOnly({ signal: controller.signal })
    .then(function () { showStatus('Tag locked.'); })
    .catch(function (err) { if (err.name !== 'AbortError') showStatus('Lock failed: ' + err.message); })
    .finally(function () { if (pendingTagOp === controller) pendingTagOp = null; });
});

Remarques : N'appelez pas WebToApk.__nfcCancelWrite() vous-même : appelé directement, il abandonne l'attente native mais ne peut pas régler la Promise de la page. Le signal fait les deux. Annuler abandonne aussi une makeReadOnly().

__nfcMakeReadOnly pont#

window.WebToApk.__nfcMakeReadOnly(): String

Met en file un verrouillage permanent (`NDEFReader.makeReadOnly()`) pour le prochain passage d'un tag ; retourne son id d'opération, "" quand le NFC est désactivé dans cette application.

Exemple

Mécanisme interne derrière NDEFReader.makeReadOnly(). Les pages utilisent new NDEFReader().makeReadOnly() ; ne l'appelez jamais directement.

Retourne : (via makeReadOnly) une Promise qui se résout après que l'utilisateur a passé un tag et qu'il est verrouillé, ou qui est rejetée avec une DOMException. Nécessite : Web NFC Support activé à l'étape 4 (Accès) lors de la compilation.

Le verrouillage est permanent. Écrivez d'abord le tag, puis verrouillez-le, et demandez à l'utilisateur avant les deux :

document.getElementById('publish-tag').addEventListener('click', async function () {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  if (!confirm('Write and lock this tag? A locked tag can never be changed.')) return;
  var reader = new NDEFReader();
  try {
    showStatus('Tap the tag to write it...');
    await reader.write({ records: [{ recordType: 'url', data: 'https://example.com/item/42' }] });
    showStatus('Now tap the same tag again to lock it...');
    await reader.makeReadOnly();
    showStatus('Tag written and locked.');
  } catch (err) {
    showStatus('Failed: ' + err.message);
  }
});

Verrouillez un tag déjà écrit, et abandonnez si aucun tag n'arrive en 20 secondes :

async function lockTag() {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  if (!confirm('Lock this tag for good? It can never be changed again.')) return;
  var controller = new AbortController();
  var timer = setTimeout(function () { controller.abort(); }, 20000);
  showStatus('Tap the tag to lock it...');
  try {
    await new NDEFReader().makeReadOnly({ signal: controller.signal });
    showStatus('Tag locked.');
  } catch (err) {
    if (err.name === 'AbortError') showStatus('No tag was tapped. Try again when ready.');
    else if (err.name === 'NotAllowedError') showStatus('This kind of tag cannot be locked.');
    else showStatus('Lock failed: ' + err.message);
  } finally {
    clearTimeout(timer);
  }
}

Lisez d'abord un tag et ne proposez de le verrouiller que s'il contient le lien attendu :

async function checkThenLock(expectedUrl) {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  var reader = new NDEFReader();
  var scanning = new AbortController();
  var lockBtn = document.getElementById('lock-btn');
  reader.onreading = function (e) {
    var rec = e.message.records.find(function (r) { return r.recordType === 'url'; });
    var url = rec ? new TextDecoder().decode(rec.data) : null;
    showStatus(url ? 'Tag points to ' + url : 'This tag holds no link.');
    lockBtn.disabled = url !== expectedUrl;
  };
  await reader.scan({ signal: scanning.signal });
  showStatus('Hold the tag to the phone to check it.');

  lockBtn.onclick = async function () {
    if (!confirm('Lock this tag? It can never be changed again.')) return;
    scanning.abort();
    showStatus('Tap the tag again to lock it...');
    try { await reader.makeReadOnly(); showStatus('Tag locked.'); }
    catch (err) { showStatus('Lock failed: ' + err.message); }
  };
}

Remarques : Chaque étape demande son propre passage de tag. L'appel retourne un id d'opération et le résultat arrive via window.__appmintNfcDone. Rejets : NotSupportedError « Tag does not support NDEF », NotAllowedError « This tag cannot be made read-only » (beaucoup de tags ne peuvent pas être verrouillés), NetworkError quand le tag s'éloigne. Lancer un write() pendant qu'un verrouillage attend remplace le verrouillage, qui est alors rejeté avec AbortError. Un signal passé à makeReadOnly({ signal }) annule l'attente.

__nfcState pont#

window.WebToApk.__nfcState(): String

"on", "off" (NFC désactivé dans les paramètres du téléphone) ou "none" (pas de NFC, ou non activé dans cette application) - ce que `NDEFReader.scan()` vérifie en premier.

Exemple

Vérification interne derrière NDEFReader.scan() : indique si cette application peut utiliser le NFC en ce moment. Les pages appellent scan() et lisent son rejet à la place.

Retourne : 'on', 'off' (le NFC est désactivé dans les paramètres du téléphone) ou 'none' (le téléphone n'a pas de NFC, ou l'application a été compilée sans Web NFC Support). Nécessite : Web NFC Support activé à l'étape 4 (Accès) lors de la compilation.

La méthode standard - scan() est rejeté avec la même réponse :

async function startReading() {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  try {
    await new NDEFReader().scan();
    showStatus('Hold a tag to the back of the phone.');
  } catch (err) {
    if (err.name === 'NotReadableError') showStatus('Turn on NFC in the phone settings, then try again.');
    else if (err.name === 'NotSupportedError') showStatus('This phone has no NFC.');
    else showStatus('NFC error: ' + err.message);
  }
}

Vérifiez une fois à l'ouverture de l'écran, pour choisir entre le panneau NFC, une invite « activer le NFC » et le masquage complet du NFC. Interrompre aussitôt le scan de la sonde ne laisse rien en cours d'exécution :

async function nfcStatus() {
  if (!('NDEFReader' in window)) return 'none';
  var probe = new AbortController();
  try {
    await new NDEFReader().scan({ signal: probe.signal });
    return 'on';
  } catch (err) {
    return err.name === 'NotReadableError' ? 'off' : 'none';
  } finally {
    probe.abort();
  }
}

async function refreshNfcUi() {
  var state = await nfcStatus();
  document.getElementById('nfc-panel').hidden = state !== 'on';
  document.getElementById('nfc-off-hint').hidden = state !== 'off';
}

refreshNfcUi();
document.addEventListener('visibilitychange', function () {
  if (document.visibilityState === 'visible') refreshNfcUi(); // back from the phone settings
});

Ne lisez les tags que pendant qu'un écran est ouvert, et distinguez un tag vide ou illisible d'un NFC désactivé :

var tagScreen = null;

async function openTagScreen() {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  tagScreen = new AbortController();
  var reader = new NDEFReader();
  reader.onreading = function (e) {
    var n = e.message.records.length;
    showStatus(n ? 'Tag ' + e.serialNumber + ' holds ' + n + ' record(s).' : 'This tag is empty.');
  };
  reader.onreadingerror = function () { showStatus('This tag cannot be read. Try another one.'); };
  try {
    await reader.scan({ signal: tagScreen.signal });
    showStatus('Hold a tag to the back of the phone.');
  } catch (err) {
    showStatus(err.name === 'NotReadableError' ? 'NFC is off. Turn it on in the phone settings.' : 'NFC error: ' + err.message);
  }
}

function closeTagScreen() {
  if (tagScreen) { tagScreen.abort(); tagScreen = null; }
}

Remarques : Mécanisme interne du polyfill ; utilisez scan(). window.AndroidNFC.startScan() répond à la même question sous forme de booléen et affiche un message « Veuillez activer le NFC » quand le NFC est désactivé.

__nfcWrite pont#

window.WebToApk.__nfcWrite(messageJson: String): String

Met en file [messageJson] (`{records:[…], overwrite}`, construit par `NDEFReader.write()`) pour le prochain passage d'un tag et retourne son id d'opération ; "" quand le NFC n'est pas activé dans cette application.

Exemple

Mécanisme interne derrière NDEFReader.write(message). Les pages utilisent new NDEFReader().write(...) ; ne l'appelez jamais directement.

Retourne : (via write) une Promise qui se résout après que l'utilisateur a passé un tag et qu'il est écrit, ou qui est rejetée avec une DOMException. Nécessite : Web NFC Support activé à l'étape 4 (Accès) lors de la compilation.

document.getElementById('write-table-tag').addEventListener('click', async function () {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  showStatus('Tap a tag to write it...');
  try {
    await new NDEFReader().write({
      records: [
        { recordType: 'url', data: 'https://example.com/menu' },
        { recordType: 'text', data: 'Table 12', lang: 'en' },
        { recordType: 'mime', mediaType: 'application/json',
          data: new TextEncoder().encode(JSON.stringify({ table: 12 })) }
      ]
    });
    showStatus('Tag written.');
  } catch (err) {
    showStatus('Write failed: ' + err.message);
  }
});

N'écrivez que sur un tag vierge, afin qu'un tag contenant déjà des données ne soit jamais remplacé par erreur :

async function writeToBlankTag(url) {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  showStatus('Tap a blank tag...');
  try {
    await new NDEFReader().write(
      { records: [{ recordType: 'url', data: url }] },
      { overwrite: false }
    );
    showStatus('Tag written.');
  } catch (err) {
    if (err.name === 'NotAllowedError') showStatus('That tag already holds data or is locked. Use a blank tag.');
    else if (err.name === 'NetworkError') showStatus('Hold the tag still until it is written.');
    else showStatus('Write failed: ' + err.message);
  }
}

Écrire un smart poster, un lien accompagné de son titre, en un seul enregistrement :

document.getElementById('write-poster').addEventListener('click', async function () {
  if (!('NDEFReader' in window)) { showStatus('NFC is not available in this app.'); return; }
  showStatus('Tap a tag to write the poster...');
  try {
    await new NDEFReader().write({
      records: [{
        recordType: 'smart-poster',
        data: { records: [
          { recordType: 'url', data: 'https://example.com/events/spring-fair' },
          { recordType: 'text', data: 'Spring Fair tickets', lang: 'en' }
        ] }
      }]
    });
    showStatus('Poster tag written.');
  } catch (err) {
    showStatus(err.name === 'NotSupportedError' ? 'This tag is too small or cannot hold NFC data.' : 'Write failed: ' + err.message);
  }
});

Remarques : Le polyfill vérifie les enregistrements, les convertit en {records:[{recordType, id, data(base64), encoding, lang, mediaType, records?}], overwrite} et transmet ici la chaîne JSON. L'appel retourne un id d'opération ; le résultat revient à la page via window.__appmintNfcDone(id, errorName, message), si bien qu'une réponse pour une écriture remplacée ne règle jamais une écriture plus récente. Les tags non formatés sont d'abord formatés. Rejets : NotAllowedError (« Tag is read-only », ou un tag contenant des enregistrements quand overwrite est faux), NotSupportedError (« Tag does not support NDEF », « Tag too small (N bytes available) »), NetworkError (le tag s'est éloigné), SyntaxError (un enregistrement que le format du tag ne peut pas contenir). L'application affiche aussi ses propres courts messages « Ready to write » et « NFC tag written ».

startScan pont#

window.AndroidNFC.startScan(): Boolean

Sonde de disponibilité héritée, présente uniquement quand le créateur a activé le NFC. Le vrai travail NFC passe par le polyfill standard NDEFReader (window.WebToApk.__nfc*), que la page doit utiliser ; celle-ci indique seulement si l'adaptateur est activé.

Décrit par son groupe, Web NFC, plutôt que séparément.

Exemple

Indique si le NFC du téléphone est activé. Il se trouve sur window.AndroidNFC, pas sur window.WebToApk, et ne lit pas de tag - la lecture se fait avec NDEFReader.

Retourne : true (de façon synchrone) quand le téléphone a le NFC et qu'il est activé ; false quand le téléphone n'a pas de NFC, ou quand le NFC est désactivé (l'application affiche alors aussi un court message « Veuillez activer le NFC »). Nécessite : Web NFC Support activé à l'étape 4 (Accès) lors de la compilation ; sans cela, window.AndroidNFC n'existe pas.

Vérifiez avant d'afficher un bouton NFC :

function nfcReady() {
  if (!window.AndroidNFC || typeof window.AndroidNFC.startScan !== 'function') return false;
  return window.AndroidNFC.startScan();
}

document.getElementById('scan-tag').addEventListener('click', async function () {
  if (!nfcReady()) { showStatus('Turn on NFC in the phone settings, then try again.'); return; }
  var reader = new NDEFReader();
  reader.addEventListener('reading', function (e) { showStatus('Tag ' + e.serialNumber); });
  await reader.scan();
  showStatus('Hold a tag to the back of the phone.');
});

Vérifiez juste avant une écriture : avec le NFC désactivé, l'écriture attendrait un passage de tag qui ne viendra jamais, alors que startScan() répond tout de suite et affiche le message « Veuillez activer le NFC » de l'application :

document.getElementById('write-pass').addEventListener('click', async function () {
  if (!window.AndroidNFC || typeof window.AndroidNFC.startScan !== 'function') {
    showStatus('NFC is not available in this app.');
    return;
  }
  if (!window.AndroidNFC.startScan()) { showStatus('NFC is off, or this phone has no NFC.'); return; }
  showStatus('Tap a tag to write it...');
  try {
    await new NDEFReader().write('Visitor pass #1042');
    showStatus('Tag written.');
  } catch (err) {
    showStatus('Write failed: ' + err.message);
  }
});

Revérifiez quand l'utilisateur revient et appuie sur « Je l'ai activé » :

var retry = document.getElementById('nfc-retry');
retry.addEventListener('click', function () {
  if (!window.AndroidNFC || typeof window.AndroidNFC.startScan !== 'function') {
    showStatus('NFC is not available in this app.');
    return;
  }
  if (window.AndroidNFC.startScan()) {
    retry.hidden = true;
    document.getElementById('nfc-panel').hidden = false;
    startReading(); // your own NDEFReader.scan() code
  } else {
    showStatus('NFC is still off, or this phone has no NFC.');
  }
});

Remarques : window.AndroidNFC existe dès le premier script de la page. Une fois que l'utilisateur a activé le NFC dans les paramètres et revient, la lecture des tags démarre toute seule.

Généré à partir de l'environnement d'exécution de l'application et de ses fichiers d'exemple à chaque compilation de la documentation. Lire en Markdown · Toutes les familles.

Vérifié par rapport au pont publié le 22/10/2026.