Why an NFC reader session has a time limit

21 September 2026

A recurring request: keep Core NFC listening. The user walks through a building tapping tags, and the app should pick each one up without a sheet appearing and disappearing every time. Is there a session configuration for that, or a way to restart the session so quickly that nobody notices?

There is not. The time limits are hard, they are documented, and the design that tries to hide them is a worse experience than the design that accepts them. Also, abusing the NFC subsystem on an iPhone will shorten the session time due to thermal stress.

What is fixed

The public CoreNFC headers state the rules plainly.

None of these has a knob. There is no entitlement that extends the timeout, no configuration that suppresses the sheet, and no background mode for reader sessions.

Why it is built this way

Reader mode is not simply listening. It is transmitting. For as long as a session is active, the phone is driving its antenna to generate the RF field that powers whatever tag comes into range, as the tag has no battery and takes its energy from the phone. The energy that goes into that field is drawn from the battery, and what the field does not deliver to a tag ends up as heat in the antenna and the circuits around it.

That is a fine thing to do for the few seconds it takes to hold a phone against a tag. It is not a fine thing to do for the twenty minutes it takes to walk through a building. So the session is shaped like the interaction it exists for: short, started by the user, visible on screen while it runs, and over quickly. The time ceiling, the single active session, and the sheet are not three separate restrictions. They are one decision, that a reader session is a burst and not a mode.

Important: Restarting a session the instant it ends does not produce a “mode" either. Trying to use it as a trick will only shorten the time for the next session until the stress on the NFC subsystem will prevent the next session from starting altogether. And for a VoiceOver user, this gets worse as a new modal is announced every time. The app is now fighting the system on the user's screen, and the user can see it.

What one session can do

A single session can do more than one read. If the use is a short guided sequence, a couple of tags within a short time, it fits inside one session without any tricks.

When the session times out, tell the user it is over and let them start the next one. Do not start it for them.

What to build instead

Match the tool to the interaction.

One tag, one tap. Start a session on a deliberate user action, a button or a gesture, read the tag, invalidate with a message, done. This is what Core NFC is for. It is also the predictable shape for VoiceOver: one action, one sheet, one result.

No app involvement at all. Late model iPhones can read NDEF tags in the background without any reader session. Encode a universal link on the tag; when the user holds the phone near it, the system shows a notification, and tapping it delivers the tag's payload to your app. There are conditions, all documented: the phone must be in use and unlocked, no reader session may be active, and the camera and Wallet must not be in use. It still requires the user to tap the notification. That is the system's version of "tap a tag while walking around", and it is as close to a live mode as iOS offers.

Proximity over minutes, hands-free. If the requirement is really "know when the user is near a point of interest" rather than "the user touched this thing", NFC is the wrong radio. It works at a few centimetres and only on request. Beacon region monitoring in Core Location, or a Bluetooth LE scan for a service the beacons advertise, work at room scale and in the background, and are what wayfinding systems use. The tags can stay as the deliberate, precise action layered on top.

Handling the endings

Every session ends in readerSession(_:didInvalidateWithError:). Treat the error as the reason, not as a failure.

ErrorWhat happenedWhat to do
readerSessionInvalidationErrorFirstNDEFTagReadRead one tag, as configuredNothing; this is success
readerSessionInvalidationErrorUserCanceledThe user tapped DoneNothing; the user is in charge
readerSessionInvalidationErrorSessionTimeoutSixty seconds elapsedTell the user; offer to start again
readerSessionInvalidationErrorSessionTerminatedUnexpectedlyThe app left the foregroundStart again when the user returns and asks
readerSessionInvalidationErrorSystemIsBusyAnother session was activeWait for it to end; do not retry in a loop

The one thing not on the list is "restart automatically". A session that begins without the user asking for it is the sheet appearing without warning, and that is the whole reason the request comes up in the first place.