Skip to content
Language: EN

How do you write and lock an NDEF tag from an iOS app?

Open an NDEF session that does not end after its first read, connect to the tag the delegate receives, and query its status and capacity before writing the whole message in one call. writeLock makes the tag read only for good. Everything happens inside one session, so the user holds the phone still until the app says it is done.

A session that stays open

Writing happens inside a reader session, so the session must not end at the first read. Apple's note on the initializer says to pass false for invalidateAfterFirstRead in a session that writes, which keeps it active until the app finishes.

The delegate implements readerSession(_:didDetect:), available from iOS 13, to receive NFCNDEFTag objects rather than messages, then calls connect(to:) on the one it wants. A tag stays connected until the app connects to another or restarts polling.

Two tags in the field are a real case. Apple's tag reader sample counts the tags before writing, asks the user through alertMessage to remove them all, and restarts polling after a short delay rather than writing to the wrong one.

Status and capacity first

queryNDEFStatus returns two values that decide whether to go on: a status, notSupported, readOnly or readWrite, and the capacity, the largest NDEF message the tag can store, in bytes.

Compare the capacity to the message before writing. A message that does not fit fails with ndefReaderSessionErrorTagSizeTooSmall, after the user has held the phone still for nothing.

The write

writeNDEF(_:) saves the whole message in one call and reports to its completion handler, or throws in its async form. Its reference ties it to the status: write only when queryNDEFStatus returned readWrite.

The errors say which step failed: ndefReaderSessionErrorTagNotWritable for a read only tag, ndefReaderSessionErrorTagSizeTooSmall for a message too long, ndefReaderSessionErrorTagUpdateFailure for an update that did not complete, and readerTransceiveErrorTagConnectionLost when the tag left the field.

After a failed update or a lost connection, read the tag again before retrying. The write may have stopped partway, the case the halfway failure page covers from the user's side.

When the write succeeds, say so in the sheet and end the session with invalidate(). Until then, the user does not know that the phone can be moved.

Locking is permanent

writeLock() changes the tag's status to read only. Its documentation explains that it updates the write access condition byte in the NDEF file control of the tag, and calls it a permanent action that cannot be undone.

Keep it a separate action from the write, taken once the content has been read back and checked on the other platform. Whether to lock at all is covered on the locking page.

What NDEF calls cannot do

The NDEF calls stop at the message. A password, a counter or a configuration page are chip commands, reached through a tag session and the tag's own protocol, such as sendMiFareCommand on a MIFARE tag, with the commands taken from the chip's datasheet.

That work leaves the NDEF session entirely, which is why the choice of session comes first, on the sessions page.

Frequently asked questions

Why does my write fail with ndefReaderSessionErrorTagSizeTooSmall?

The message is larger than the tag can store. queryNDEFStatus returns the capacity in bytes, so compare the two before writing.

Can a locked tag be unlocked from iOS?

No. Apple describes writeLock as a permanent action that cannot be undone.

Can an iPhone app set a tag password?

Not with the NDEF calls. A password is set with chip commands, sent in a tag session through the tag's protocol, with the commands taken from the chip's datasheet.

Why must the session stay open after reading?

The write happens inside it. With invalidateAfterFirstRead set to true, the session ends after the first read and leaves nothing to write with.