●NATIVE — Rork Max reaches AR and LiDAR scanning, Metal-backed 3D, Dynamic Island, Siri Intents, HealthKit, NFC, App Clips, and on-device Core ML●IOS27 — iOS 27 Developer Beta 4 extends Siri AI to iPhone 15 Pro and 15 Pro Max plus the 16 and 17 lines, with noticeably faster responses than the first beta●DESIGN — Apple's own design kits for iOS, iPadOS, and macOS 27 are now available for Figma and Sketch, ready for when you lay out UI for the new OS●CANARY — Android 17, codenamed Cinnamon Bun, retires Developer Previews in favor of continuously updated Canary builds, which changes when you schedule testing●SPARK — Gemini Spark in Android 17 drives apps directly to automate multi-step errands like booking a ride or placing an order●SCALE — Rork raised $2.8M from a16z and now draws roughly 743,000 visits a month●NATIVE — Rork Max reaches AR and LiDAR scanning, Metal-backed 3D, Dynamic Island, Siri Intents, HealthKit, NFC, App Clips, and on-device Core ML●IOS27 — iOS 27 Developer Beta 4 extends Siri AI to iPhone 15 Pro and 15 Pro Max plus the 16 and 17 lines, with noticeably faster responses than the first beta●DESIGN — Apple's own design kits for iOS, iPadOS, and macOS 27 are now available for Figma and Sketch, ready for when you lay out UI for the new OS●CANARY — Android 17, codenamed Cinnamon Bun, retires Developer Previews in favor of continuously updated Canary builds, which changes when you schedule testing●SPARK — Gemini Spark in Android 17 drives apps directly to automate multi-step errands like booking a ride or placing an order●SCALE — Rork raised $2.8M from a16z and now draws roughly 743,000 visits a month
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.