NDEF
CoreExtendedNFC routes NDEF reads and writes through the best family-specific path after tag identification.
Read
let message = try await CoreExtendedNFC.readNDEF(info: info, transport: transport)
A tag without an NDEF Capability Container (Type 2 CC0 other than E1h, Type 5 CC magic other than E1h / E2h, or no Type 4 NDEF Tag Application) throws NFCError.tagNotNDEFFormatted. A formatted tag without a readable NDEF TLV, or one holding an empty message (an erased tag: 03 00 on Type 2 / Type 5, NLEN = 0 on Type 4, Ln = 0 on Type 3), throws NFCError.ndefMessageNotFound on every family. On Type 5 tags, a CC with a major version above 1 throws tagNotNDEFFormatted and one that denies read access (CC byte 1 b3-b2 ≠ 00b) throws NFCError.tagLocked. readNDEF reads only the CC and the blocks covering the T5T area announced by MLEN, not the whole tag. Blocks from 256 on are read with EXTENDED READ SINGLE BLOCK; if the tag rejects it, a message that ends past block 255 is reported as ndefMessageNotFound.
On Type 2 tags the message is read up to the data area the CC declares (CC byte 2 × 8 bytes from page 4, NFC Forum T2T 1.0 §6.1). When the product is not identified, its memory map covers only pages 0-15 (NTAG I2C, and NTAG203 and other tags that do not answer GET_VERSION), so readNDEF reads the further pages the CC declares, up to page 255, and stops at the first page the tag refuses. Writes still keep to the memory map. Lock Control (01h) and Memory Control (02h) TLVs are honoured as NFC Forum T2T 1.0 §2.3.4 / §2.3.5 require: the lock and reserved bytes they declare (PageAddr × 2BytesPerPage + ByteOffset; the size counts bits for Lock Control and bytes for Memory Control) are skipped while the NDEF TLV is read, even when they fall inside the message.
Parsing is strict, like Android's NdefMessage: a message whose first record lacks MB or in which a later record (a chunk included) sets MB (NDEF 1.0 §3.2.1), a message whose last record lacks ME or with bytes after the record carrying ME (§3.2.2), a record that breaks the NDEF 1.0 §3.2.6 field rules (TNF Empty with a type, ID or payload; TNF Unknown or Unchanged with a type; TNF Unchanged outside a chunk sequence) are rejected with NFCError.invalidResponse. The reserved TNF 07h is read as Unknown, as §3.2.6 recommends. The tag readers hand the parser exactly the NDEF TLV value (Type 2 / Type 5), NLEN / ENLEN bytes (Type 4) or Ln bytes (Type 3).
An absolute-URI record (TNF 03h) carries its URI in the TYPE field, so parsedPayload returns .uri with the TYPE decoded as UTF-8. A Smart Poster without a URI record takes its uri from an absolute-URI child record.
Write
try await CoreExtendedNFC.writeNDEF(message, info: info, transport: transport)
// Erase: a message with no records is written as an empty NDEF message.
try await CoreExtendedNFC.writeNDEF(NDEFMessage(records: []), info: info, transport: transport)
Records that cannot be encoded are refused before anything is written, and so are records that break the NDEF 1.0 §3.2.6 field rules (TNF Empty with a type, ID or payload, TNF Unknown with a type, TNF Unchanged or the reserved TNF 07h): validatedData() throws NFCError.invalidParameter. An empty message is written as 03 00 FE on Type 2 and Type 5, NLEN = 0 on Type 4, and Ln = 0 on Type 3, which erases the tag.
Type 2, Type 4 and Type 5 writes follow the NFC Forum write procedure: the message length (TLV length or NLEN / ENLEN) is first written as zero, then the message, and the real length last, so a write interrupted by the tag leaving the field reads back as an empty message, never a truncated one. When a 3-byte TLV length (FF hi lo) spans two blocks, the block holding its first byte is written last, so the switch to the real length is one block write. On Type 4, the length is written in one UPDATE BINARY only when MLc covers it (MLc ≥ 2 for NLEN, ≥ 4 for ENLEN); a card with a smaller MLc gets the length in pieces, and a write interrupted between them can leave a wrong length. Type 2 and Type 5 messages are limited to FFFEh bytes, the largest 3-byte TLV length.
- Type 2: the CC on page 3 is read first. The tag must be NDEF formatted (CC0 =
E1h, mapping major version 1) and grant write access, otherwisetagNotNDEFFormattedortagLockedis thrown. Capacity is CC2 × 8 bytes, capped at the product's user memory. NTAG215/216 advertise less than their full user memory (CC23Eh/6Dh). A message that fills the data area exactly is written without the optional terminator TLV. The NDEF TLV is written at the start of page 4, after any NULL, Lock Control and Memory Control TLVs found there, which are kept byte for byte (pages 4-6 are read with the CC, and pages 7-10 too when a control TLV runs past page 6). Control TLVs are kept only when each has a length of 3 and they are followed by an NDEF or Terminator TLV, or only by NULL TLVs, within those pages; anything else at page 4, such as earlier non-NDEF data, is overwritten. When a control TLV declares lock or reserved bytes inside the data area, the message and its padding stop before the page holding the first of them; a tag whose reserved bytes share a page with the control TLVs is refused withunsupportedOperation. NXP Ultralight and NTAG tags keep their lock and configuration bytes outside the data area, so their capacity is unchanged apart from the bytes a preserved control TLV occupies. - Type 5: the CC is read first and must be formatted (
E1h/E2h, major version 1, write access granted). The message is bounded by MLEN × 8 bytes. Blocks above 255 are read and written with the extended ISO 15693 commands; tags that reject them are limited to blocks 0-255. The NDEF TLV starts right after the CC (byte 4 or 8), whatever the block size. Only the blocks holding the NDEF TLV and its terminator are written; the CC and memory after the terminator are untouched. A tag whose CC requires the special frame format (CC byte 3 b4) is refused withunsupportedOperation, because CoreNFC cannot reliably send it. - Type 4: CC write access must be
00h. Both NLEN (T4T 2.0) and ENLEN (T4T 3.0) NDEF files are supported, up to 32 KB. When the card refuses the NDEF Tag ApplicationD2760000850101, the mapping 1.0 applicationD2760000850100is tried, and its files are then selected with P2 =00h.
A message that does not fit throws NFCError.ndefCapacityExceeded(required:capacity:).
Format
formatNDEF(info:transport:) supports Type 2 and Type 5 (ISO/IEC 15693) tags; other families throw NFCError.tagNotSupported. The Type 3 Attribute Information Block and the Type 4 CC file are managed by the tag. Type 2 formatting clears the user pages first and writes the CC (OTP) last. Type 5 formatting writes a fresh CC and an empty message and clears the rest of the data area. On a tag with more than 256 blocks, the NDEF area covers the whole memory: the CC magic is E2h and blocks from 256 on are written with EXTENDED WRITE SINGLE BLOCK, once an extended read of the last block shows the tag accepts the extended commands. Otherwise, and on tags with 256 blocks or fewer, the area covers blocks 0-255 and the CC magic is E1h. Every block address is planned before the first write. The block count comes from EXTENDED GET SYSTEM INFORMATION (3Bh) when GET SYSTEM INFORMATION reports 256 blocks or no memory size.
| Family | Path |
|---|---|
| Ultralight / NTAG | Type 2 TLV and Capability Container over page reads/writes. |
| FeliCa | Type 3 NDEF service reads and writes. |
| Type 4 | ISO 7816 SELECT, Capability Container, and READ / UPDATE BINARY. |
| ISO 15693 | Type 5 NDEF mapping over block reads and writes; extended commands for blocks from 256 on when the tag accepts them. |