Skip to main content
A Tag handed to you by a session is deliberately almost empty. It has an identifier, a list of technologies, a platform, and two platform facets. It has no technology methods at all until you narrow it.
That is not ceremony. It is what makes the difference between the platforms something your editor tells you about:
On iOS tag.is('mifareClassic') is always false, because CoreNFC cannot reach Crypto-1 at any OS version. Not undefined, not a method that throws when called — a guard that does not match, so the branch you wrote for Android is simply not entered.

What is on every tag

Uint8Array | null
The UID, or null when the platform does not expose one.
string | null
The same as lowercase hex. Convenience for logging and comparison — and on the web, where Chrome reports the serial with colons, this is normalised so it reads the same everywhere.
TagTech[]
The technologies this tag actually supports, here, on this platform. Worth logging in a bug report.
NfcPlatform
ios, android or web.
boolean
Whether the tag is gone: released, lost, or its session closed.
(tech: TagTech) => boolean
The type guard. This is the only way to reach a technology’s methods.
(listener: () => void) => Subscription
Fires when the tag leaves the field. Android only — capabilities.tagLost says whether the platform delivers this directly or it is polled for, which is a difference of latency. CoreNFC has no removal callback at all, so on iOS a tag that has gone surfaces as the next operation failing with tagLost.

The technologies, and what each unlocks

Typed helpers over transceive — ISO 7816 with chaining, ISO 15693, FeliCa, NTAG password auth — live in /protocols as plain TypeScript.

NDEF

Decoding happens in TypeScript, from the raw bytes, using the same codec on both platforms. That is deliberate: chunk reassembly, UTF-16 text records and malformed-input handling then behave identically everywhere, rather than inheriting whatever each platform’s own NDEF parser happens to do.
readNdefBytes is the escape hatch for a tag this library’s decoder rejects: you still get the bytes and can decide what to do with them.

ISO-DEP, and the APDU shape

The shape is identical on both platforms. In the library this one replaces, the same call returned [...bytes, sw1, sw2] on iOS and the raw bytes on Android — with a // TODO: make following data the same format as Android in its own source admitting it. If you have platform branches around a transceive, delete them.

Raw exchange

A command longer than maxTransceiveLength rejects with transceiveTooLong rather than being truncated.

Platform facets

tag.android and tag.ios are undefined on the other platform, so a facet you forgot to guard is a type error rather than a crash.
Declaring D2760000850101, the NDEF application AID, in your iOS entitlements changes how CoreNFC presents cards that support it: a DESFire card then arrives as ISO 7816 rather than MIFARE, so tag.is('mifareUltralight') and the MIFARE-specific commands stop matching. tag.ios?.coreNfcType is where you see that happening.

How long a tag is valid

A tag is valid for exactly as long as the session that produced it, and using one afterwards rejects with sessionClosed.
There is no version of these APIs that hands you a native handle and trusts you to give it back. A handle that outlives its scope is a leak whose failure surfaces somewhere else entirely — which is precisely the class of bug this library exists to remove.

Tags that go away mid-operation

On Android, capabilities.tagLost reports native when the platform delivers removal directly and polled when it is checked for on an interval. On iOS it is none: the departure surfaces as the next operation rejecting with tagLost, which is recoverable, so a retry is worth offering.

Checking a device rather than a tag

No heuristics are involved. The library this one replaces probed /dev/bcm2079x-i2c and /dev/pn544, scanned /system/lib, and carried a hardcoded special case for one Lenovo model — all of it guesswork about a question the platform answers directly.

Next

The NDEF codec

Every record type, chunking, and tag-level framing.

Protocols

ISO 7816, ISO 15693, FeliCa and NTAG over one primitive.