Buat APK Gratis

API jembatan JavaScript

NFC

Membaca tag lewat window.AndroidNFC, dan sisi penulisan di balik polyfill NDEFReader.

NDEFReader standar web#

new NDEFReader() — Web NFC

Contoh

Web NFC standar: new NDEFReader() membaca, menulis, dan mengunci tag NFC di aplikasi yang terpasang, dengan kode yang sama yang diterima Chrome di Android.

Mengembalikan: Promise dan event reading / readingerror, seperti di standar web. Rekaman berupa NDEFRecord: recordType ('text', 'url', 'absolute-url', 'mime', 'smart-poster', 'empty', 'unknown' atau tipe eksternal seperti 'example.com:thing'), mediaType (rekaman mime), id, encoding dan lang (rekaman teks) dan data sebagai DataView. Membutuhkan: aktifkan Web NFC Support di Langkah 4 (Access) saat Anda membuat build. Tanpa itu NDEFReader tidak ada. Tidak ada permintaan izin saat berjalan; navigator.permissions.query({name:'nfc'}) menjawab granted.

Baca tag. Dekode data dengan TextDecoder, persis seperti di 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
});

Tulis teks atau tautan. Promise terselesaikan setelah pengguna mengetuk 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
  }
}

Kunci tag selamanya. makeReadOnly() tidak dapat dibatalkan - tanyakan dulu:

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); }
}

Catatan: Tipe rekaman untuk write: text (string, atau byte dengan encoding), url dan absolute-url (string), mime (byte + mediaType), smart-poster ({records: [...]}), tipe eksternal 'domain:thing' (byte atau {records: [...]}), unknown (byte) dan empty. String biasa menulis rekaman teks; byte biasa menulis rekaman mime application/octet-stream. Hanya pembaca yang memanggil scan() yang menerima event reading; AbortController.abort() pada signal menghentikan pembaca itu (tidak ada stop() di standar). readingerror dipicu untuk tag yang tidak dapat menampung data NFC. Ponsel menunggu SATU operasi tag pada satu waktu: write() atau makeReadOnly() baru saat satu sedang menunggu akan menggantikannya, dan panggilan yang digantikan ditolak dengan AbortError (Chrome akan mengantrekannya). Sebuah signal pada write() / makeReadOnly() membatalkan penantian. Kesalahan: NotSupportedError (tidak ada NFC, atau tag tidak dapat menampung NDEF, atau terlalu kecil), NotReadableError (NFC dimatikan), NotAllowedError (tag hanya-baca, atau overwrite: false pada tag yang berisi rekaman), NetworkError (tag menjauh), TypeError (rekaman yang tidak diizinkan standar). NDEFReader ada di halaman utama, bukan di dalam iframe.

__nfcCancelWrite jembatan#

window.WebToApk.__nfcCancelWrite()

Membatalkan penulisan/penguncian yang sedang menunggu (polyfill memanggilnya saat `signal` operasi dibatalkan).

Contoh

Membuang penulisan atau penguncian NFC yang diantrekan agar ketukan tag berikutnya dibaca seperti biasa. Halaman membatalkan lewat standar web - sebuah AbortController signal - dan polyfill NDEFReader memanggil ini untuk mereka.

Mengembalikan: tidak ada. Membutuhkan: Web NFC Support diaktifkan di Langkah 4 (Access) saat Anda membuat build.

Cara standar, yang dipatuhi aplikasi - write() yang menunggu ditolak dengan 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();
});

Menyerah sendiri bila tidak ada tag yang datang tepat waktu; timer yang membatalkan signal membatalkannya dengan cara yang sama:

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);
  }
}

Batalkan penguncian (atau penulisan) yang menunggu saat pengguna meninggalkan layar, agar ketukan berikutnya tidak mengubah 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; });
});

Catatan: Jangan panggil WebToApk.__nfcCancelWrite() sendiri: bila dipanggil langsung, ia membuang penantian native tetapi tidak dapat menyelesaikan Promise halaman. signal melakukan keduanya. Pembatalan juga membuang antrean makeReadOnly().

__nfcMakeReadOnly jembatan#

window.WebToApk.__nfcMakeReadOnly(): String

Mengantrekan penguncian permanen (`NDEFReader.makeReadOnly()`) untuk ketukan tag berikutnya; mengembalikan ID operasinya, "" bila NFC mati di aplikasi ini.

Contoh

Saluran internal di balik NDEFReader.makeReadOnly(). Halaman memakai new NDEFReader().makeReadOnly(); jangan pernah memanggil ini secara langsung.

Mengembalikan: (lewat makeReadOnly) sebuah Promise yang terselesaikan setelah pengguna mengetuk tag dan tag terkunci, atau ditolak dengan DOMException. Membutuhkan: Web NFC Support diaktifkan di Langkah 4 (Access) saat Anda membuat build.

Penguncian bersifat permanen. Tulis tag dulu, lalu kunci, dan tanyakan pengguna sebelum keduanya:

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);
  }
});

Kunci tag yang sudah ditulis, dan menyerah bila tidak ada tag yang datang dalam 20 detik:

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);
  }
}

Baca tag lebih dulu dan tawarkan untuk menguncinya hanya bila isinya adalah tautan yang diharapkan:

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); }
  };
}

Catatan: Setiap langkah membutuhkan ketukannya sendiri. Panggilan mengembalikan ID operasi dan hasilnya tiba lewat window.__appmintNfcDone. Penolakan: NotSupportedError "Tag does not support NDEF", NotAllowedError "This tag cannot be made read-only" (banyak tag tidak dapat dikunci), NetworkError saat tag menjauh. Memulai write() saat sebuah penguncian menunggu akan menggantikan penguncian itu, yang lalu ditolak dengan AbortError. Sebuah signal yang diberikan ke makeReadOnly({ signal }) membatalkan penantian.

__nfcState jembatan#

window.WebToApk.__nfcState(): String

"on", "off" (NFC dimatikan di pengaturan ponsel) atau "none" (tidak ada NFC, atau tidak diaktifkan di aplikasi ini) - hal pertama yang diperiksa `NDEFReader.scan()`.

Contoh

Pemeriksaan internal di balik NDEFReader.scan(): apakah aplikasi ini dapat memakai NFC sekarang. Halaman memanggil scan() dan membaca penolakannya.

Mengembalikan: 'on', 'off' (NFC dimatikan di pengaturan ponsel) atau 'none' (ponsel tidak memiliki NFC, atau aplikasi dibuat tanpa Web NFC Support). Membutuhkan: Web NFC Support diaktifkan di Langkah 4 (Access) saat Anda membuat build.

Cara standar - scan() ditolak dengan jawaban yang sama:

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);
  }
}

Periksa sekali saat layar terbuka, untuk memilih antara panel NFC, petunjuk "nyalakan NFC", dan menyembunyikan NFC sepenuhnya. Membatalkan pemindaian pemeriksa segera tidak menyisakan proses yang berjalan:

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
});

Baca tag hanya saat satu layar terbuka, dan bedakan tag kosong atau tak terbaca dari NFC yang mati:

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; }
}

Catatan: Saluran internal untuk polyfill; gunakan scan(). window.AndroidNFC.startScan() menjawab pertanyaan yang sama sebagai boolean dan menampilkan pesan "Please enable NFC" bila NFC mati.

__nfcWrite jembatan#

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

Mengantrekan [messageJson] (`{records:[…], overwrite}`, dibuat oleh `NDEFReader.write()`) untuk ketukan tag berikutnya dan mengembalikan ID operasinya; "" bila NFC tidak diaktifkan di aplikasi ini.

Contoh

Saluran internal di balik NDEFReader.write(message). Halaman memakai new NDEFReader().write(...); jangan pernah memanggil ini secara langsung.

Mengembalikan: (lewat write) sebuah Promise yang terselesaikan setelah pengguna mengetuk tag dan tag selesai ditulis, atau ditolak dengan DOMException. Membutuhkan: Web NFC Support diaktifkan di Langkah 4 (Access) saat Anda membuat build.

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);
  }
});

Tulis hanya ke tag kosong, agar tag yang sudah berisi data tidak tertimpa karena kesalahan:

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);
  }
}

Tulis smart poster, yaitu tautan beserta judulnya, sebagai satu rekaman:

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);
  }
});

Catatan: Polyfill memeriksa rekaman, mengubahnya menjadi {records:[{recordType, id, data(base64), encoding, lang, mediaType, records?}], overwrite} dan meneruskan string JSON ke sini. Panggilan mengembalikan ID operasi; hasilnya kembali ke halaman lewat window.__appmintNfcDone(id, errorName, message), sehingga jawaban untuk penulisan yang sudah digantikan tidak pernah menyelesaikan yang lebih baru. Tag yang belum diformat diformat terlebih dahulu. Penolakan: NotAllowedError ("Tag is read-only", atau tag berisi rekaman saat overwrite bernilai false), NotSupportedError ("Tag does not support NDEF", "Tag too small (N bytes available)"), NetworkError (tag menjauh), SyntaxError (rekaman yang tidak dapat ditampung format tag). Aplikasi juga menampilkan pesan singkatnya sendiri "Ready to write" dan "NFC tag written".

startScan jembatan#

window.AndroidNFC.startScan(): Boolean

Pemeriksaan ketersediaan lama, hanya ada bila pembuat mengaktifkan NFC. Pekerjaan NFC yang sebenarnya berjalan lewat polyfill NDEFReader standar (window.WebToApk.__nfc*), yang sebaiknya dipakai halaman; ini hanya melaporkan apakah adapternya menyala.

Dijelaskan oleh grupnya, Web NFC, bukan sendiri.

Contoh

Memberi tahu Anda apakah NFC ponsel menyala. Ini ada di window.AndroidNFC, bukan di window.WebToApk, dan tidak membaca tag - pembacaan dilakukan dengan NDEFReader.

Mengembalikan: true (secara sinkron) bila ponsel memiliki NFC dan NFC menyala; false bila ponsel tidak memiliki NFC, atau NFC mati (maka aplikasi juga menampilkan pesan singkat "Please enable NFC"). Membutuhkan: Web NFC Support diaktifkan di Langkah 4 (Access) saat Anda membuat build; tanpa itu window.AndroidNFC tidak ada.

Periksa sebelum menampilkan tombol 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.');
});

Periksa tepat sebelum menulis: saat NFC mati, penulisan akan menunggu ketukan yang tidak pernah datang, sedangkan startScan() menjawab seketika dan menampilkan pesan "Please enable NFC" dari aplikasi:

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);
  }
});

Periksa lagi saat pengguna kembali dan mengetuk "I've turned it on":

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.');
  }
});

Catatan: window.AndroidNFC ada sejak skrip pertama di halaman. Setelah pengguna menyalakan NFC di pengaturan dan kembali, pembacaan tag dimulai dengan sendirinya.

Dibuat dari runtime aplikasi dan file contohnya pada setiap build dokumentasi. Baca sebagai Markdown · Semua keluarga.

Diperiksa terhadap jembatan yang dirilis pada 2026-10-22.