●SDK58 — The Expo SDK 58 beta is open. It ships the React Native 0.88 release candidate, and the beta period is stated as three to four weeks●11/01 — For anyone who requested an extension, Google Play's target API deadline lands on November 1. Forty-four days out●EASENV — A long-open report: secrets handed to a local build arrive as the literal variable name rather than its value, and the damage surfaces much later●NEW — The replacement the table recommended had already shut down. A record of reconciling all 74 rows of the deprecation list●UISCENE — iOS 27 requires the new scene lifecycle. SDK 57 makes it something you opt into; it only becomes the default in 58●CREDIT — What "AI errors don't cost credits" actually covers becomes clear once you record a day of asking for the same fix more than once●SDK58 — The Expo SDK 58 beta is open. It ships the React Native 0.88 release candidate, and the beta period is stated as three to four weeks●11/01 — For anyone who requested an extension, Google Play's target API deadline lands on November 1. Forty-four days out●EASENV — A long-open report: secrets handed to a local build arrive as the literal variable name rather than its value, and the damage surfaces much later●NEW — The replacement the table recommended had already shut down. A record of reconciling all 74 rows of the deprecation list●UISCENE — iOS 27 requires the new scene lifecycle. SDK 57 makes it something you opt into; it only becomes the default in 58●CREDIT — What "AI errors don't cost credits" actually covers becomes clear once you record a day of asking for the same fix more than once
Adding NFC Tag Reading to a Rork Max App with Core NFC
How to add Core NFC to a Swift app generated by Rork Max and read NDEF tags, covering the entitlement and Info.plist setup through the production gotchas, with real code.
While building a stamp-card app, someone asked me to record store visits by tapping a physical tag instead of a QR code. Launching the camera and lining up a code is surprisingly fiddly in the few seconds you have at a counter. With an NFC tag, a tap is all it takes.
Rork Max generates the Swift app, but a feature like Core NFC that involves an Apple entitlement will not run from the generated code alone. Unless you fix up the signing and the plist by hand, the build succeeds yet the session closes instantly on a device. Here is the order I used as an indie developer, including where I tripped.
Set the Entitlement and Info.plist First
Configuration comes before code. Miss this and no amount of correct code will run. Three things are required.
Enable the Near Field Communication Tag Reading capability on your App ID in the Apple Developer portal.
Add an .entitlements file and put NDEF into com.apple.developer.nfc.readersession.formats.
Add NFCReaderUsageDescription to Info.plist and explain why you use NFC.
Reviewers read that third string. "Reads tags" is vague and invites a rejection. I write something that shows the use case, like "Reads the NFC tag at the register to record visit points."
With configuration done, build the reader. Make a small reader class that owns an NFCNDEFReaderSession and can be called from the screen Rork Max generated.
import CoreNFCfinal class TagReader: NSObject, ObservableObject, NFCNDEFReaderSessionDelegate { @Published var lastPayload: String? private var session: NFCNDEFReaderSession? func begin() { guard NFCNDEFReaderSession.readingAvailable else { print("NFC not available on this device") return } session = NFCNDEFReaderSession( delegate: self, queue: nil, invalidateAfterFirstRead: true ) session?.alertMessage = "Hold the top of your iPhone near the tag" session?.begin() } func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) { guard let record = messages.first?.records.first else { return } let text = decodeText(from: record) ?? "(empty)" DispatchQueue.main.async { self.lastPayload = text } } func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) { // User cancel and read failures also land here print("session invalidated: \(error.localizedDescription)") }}
With invalidateAfterFirstRead: true the session closes after one tag. To read several in a row, set it to false and call invalidate() yourself. For one-at-a-time cases like a stamp card, true is the straightforward choice.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A checklist for the three required entitlement and Info.plist settings so none are missed
✦Complete working code for reading with NFCNDEFReaderSession and its delegate
✦Fixes for read failures, iPhone antenna placement, and background reading in production
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
An NDEF text record carries the length of the language code in its first byte. Stringify the payload without knowing that and you get garbage at the front.
For a URL record (type 0x55), the first byte is a prefix code like https://www. instead. Knowing that text and URL records decode differently up front saves you a wasted investigation.
What Bit Me in Production
These are the quirks that surfaced only once real tags met real registers, listed in the order they cost me time.
The iPhone antenna sits at the top of the device. Many users try to touch the middle of the screen to the tag and fail. Just spelling out "hold the top near it" in alertMessage visibly cut my read-failure questions.
Core NFC does nothing in the simulator. readingAvailable always returns false, so keep one physical device on hand from day one.
Background tag reading (reacting to a tap without launching the app) needs a special format with a Universal Link and is a different mechanism from NFCNDEFReaderSession. If the requirement says "without opening the app," do not confuse the two.
A user cancel also arrives at didInvalidateWithError. Show "read failed" without checking the error code and you flash an error at someone who deliberately closed it. Check for NFCReaderError.readerSessionInvalidationErrorUserCanceled and show nothing in that case; that is the production-safe handling.
Choosing and Writing Tags in Practice
Once the reading side is solid, the next step is the practical question of which tag to use. Off-the-shelf NFC tags come in several standards, but for general stamp use NTAG213 or NTAG215 are safe picks. Their small capacity keeps them cheap and is plenty for text or a short URL. In my case I hand out NTAG213 tags written with a per-store identifier, keeping the unit cost to a few tens of yen.
Writing is the reverse of reading. You can push writeNDEF to a tag through connect on an NFCNDEFReaderSession, but writing one at a time on an iPhone is not realistic at volume. I batch-write with a dedicated tool and keep the shipping app read-only. Splitting the roles keeps the production app's capability limited to reading and makes the review explanation simpler.
On reliability, sticking a tag directly to a metal surface drops the read rate, because metal absorbs the radio field. Use a metal-rated tag (one with a ferrite layer on the back) or just put a few millimeters of non-metal between them. The bulk of "stuck it on the metal fixture by the register and it will not read" questions traced back to this placement. Only after testing the physical mounting surface too does it hold up in real use.
What to Delegate to Rork Max, What to Hand-Write
My call is to hand-write the NFC reading logic itself. The entitlement and decoding rules are rigid, so writing it correctly the first time beats round trips between generation and fixes. The result screen and the stamp-collecting animation, on the other hand, I draft with Rork Max and refine from there.
NFC is not a flashy feature, but it shines for "instant, on the spot" moments at counters and events. On a different axis from a revenue pillar like AdMob, it pays back slowly as repeat rate. The tactile feel of bridging the physical world and the app with a single tap is something I could not give up once I added it.
I hope this helps anyone working on the same problem.
Share
Thank You for Reading
Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.