NFC on Chrome for Android

Joel Oliveira
Joel Oliveira
Nov 12 2021
Posted in Engineering & Technology

Quick intro into Web NFC

NFC on Chrome for Android

In this week's post, we would like to introduce you to Web NFC. Reading and writing to NFC tags is now possible straight from a web page. All you need is Chrome 89+ installed in your NFC-capable Android device.

What is Web NFC?

NFC stands for Near Field Communications, a wireless technology which can be used for contactless exchange of data over short distances. It operates at 13.56 MHz and enables communication between devices at a distance less than 10 cm (4 inches) and a transmission rate ranging from 106 to 424 kbit/s.

Web NFC enables web pages to read and write to NFC tags when they are in close proximity to a NFC capable Android device using Chrome. Currently only supports NFC Data Exchange Format (NDEF), a lightweight binary message format that works across different tag formats.

An NFC tag is powered by magnetic induction when an active NFC device (e.g. a phone) is in proximity. NFC tags come in many forms, as stickers, cards, wearables, you name it.

Using the Web NFC API

Because this API is not available in all browsers, feature detection is the very first step you have to take to interact with this API:

if ('NDEFReader' in window) {
    // We can use Web NFC
}

Detecting the presence of the NDEFReader object is not a guarantee that you can scan and write NFC Tags, tho. Basically it tells you that the browser supports this feature but not whether the required hardware is available. If a device does not have NFC enabled or hardware is missing, the promise returned by certain methods will reject.

if ('NDEFReader' in window) {

    const ndef = new NDEFReader();

    ndef.scan().then(() => {

      ndef.onreadingerror = () => {
           // Could not scan a tag, try another
      };

      ndef.onreading = event => {
        //Scanned a tag successfully
      };

    }).catch(error => {
      /// Something wrong with hardware
    });

}

Web NFC is also only available to top-level frames and secure browsing contexts (HTTPS only). Web pages must first request the nfc permission while handling a user gesture (for example, a button click). The NDEFReader scan() and write() methods will trigger a dialogue, if access was not previously granted:

Basically, this translates to the following code:

if ('NDEFReader' in window) {

    navigator.permissions.query({ name: "nfc" }).then((nfcStatus) => {

        if (nfcStatus.state === "granted") {
            startScanning();
        } else {

          document.querySelector("#scanButton").onclick = event => {
            startScanning();
          };

        }

    });

    function startScanning(){
        const ndef = new NDEFReader();

        ndef.scan().then(() => {

          ndef.onreadingerror = () => {
             // Could not scan a tag, try another
          };

          ndef.onreading = event => {
            //Scanned a tag successfully
          };

        }).catch(error => {
          /// Something wrong with hardware
        });
    }

}

It is also important to note that scanning or writing operations are only available when users touch a NFC Tag and NFC radio signals are blocked when the display is off or the device is locked. Non-visible web pages are also not able to receive or push any content to a NFC Tag. Luckily, you can use the Page Visibility API to track when document visibility changes:

document.onvisibilitychange = event => {
  if (document.hidden) {
    // All NFC operations are suspended.
  } else {
    // All NFC operations can  be resumed.
  }
}

Read & Write to NFC Tags

Anyone can read, and unless it is read-only, anyone can write to a NDEF-capable NFC tag. An NFC Tag can store a NDEF message which encapsulates one or more NDEF records. Each NDEF record is a binary structure that contains a certain data payload, and associated type information. Web NFC supports the following NDEF record types: empty, text, URL, smart poster, MIME type, absolute URL, external type, unknown, and local type.

In this post, we will not cover all of these types, instead we'll focus on two good examples. You can always find all the information you need in the Web NFC Specification.

Read a Text Record

The text record data can be decoded with a TextDecoder instantiated with the record encoding. And the language of a text record is also available through its lang property:

... more code

ndef.scan().then(() => {

... more code

    ndef.onreading = event => {
        if (event.recordType === "text") {
            const textDecoder = new TextDecoder(event.encoding);
            console.log(`Text Record: ${textDecoder.decode(event.data)} ${record.lang}`);
        }
    }

});

... more code

Write a Text Record

Text records are UTF-8 by default and assume the current document's language but both encoding and lang can be specified in a custom NDEF record:

... more code

const ndef = new NDEFReader();

function a2utf16(string) {
  let result = new Uint16Array(string.length);
  for (let i = 0; i < string.length; i++) {
    result[i] = string.codePointAt(i);
  }
  return result;
}

const textRecord = {
  recordType: "text",
  lang: "pt",
  encoding: "utf-16",
  data: a2utf16("Olá Joel")
}

ndef.write({ records: [textRecord] }).then(() => {
    // Successfully changed NFC Tag
}).catch(() => {
    // Cannot write to a NFC Tag
});

... more code

Read an URL

Pretty much the same way, you can use a TextDecoder to decode an URL record:

... more code

ndef.scan().then(() => {

... more code

    ndef.onreading = event => {
        if (event.recordType === "url") {
            const textDecoder = new TextDecoder();
            console.log(`URL: ${textDecoder.decode(event.data)}`);
        }
    }

});

... more code

Write an URL

And to write an URL record you can use an object containing a recordType and data keys:

... more code

const ndef = new NDEFReader();

ndef.write({ records: [{recordType: "url", data: "https://notificare.com/contact"}] }).then(() => {
    // Successfully changed NFC Tag
}).catch(() => {
    // Cannot write to a NFC Tag
});

... more code

Abort NFC Operations

Finally, you will also find that, in certain cases, you will want to cancel NFC operations. For example, you might want to gives users the option to change their mind after they hit that write NFC Tag button.

Just like with other modern APIs, the AbortController primitive is available in Web NFC. By using the example below, you can simultaneous cancel both a scan and write operation on-demand:

... more code

const abortController = new AbortController();
abortController.signal.onabort = event => {
  // All NFC operations were aborted
};

ndef.scan({ signal: abortController.signal }).then(() => {
    ... more code
});

ndef.write("My Text Record", { signal: abortController.signal }).then(() => {
    ... more code
});

document.querySelector("#abortButton").onclick = event => {
  abortController.abort();
}

... more code

Neat stuff, right?

NFC and NDEF encoded tags are already part of our mobile SDKs. Web NFC adoption by major browsers can also bring the power of NFC to the web. For example, websites running on mobile devices, with NFC enabled, could scan NFC Tags to display additionally content about a product in your store, a painting on an exhibit or a visitor's badge on an event.

As always, we hope you liked this article and if you have anything to add, we are available via our Support Channel.

Keep up-to-date with the latest news