iOS Core NFC Programming Guide
Apple introduced Core NFC in iOS 11 and has progressively expanded its capabilities with each major release. As of iOS 15, iPhones can read and write most NFC ForumNFC ForumIndustry body developing NFC standards, specifications, and certifications since 2004View full → tag types, though some restrictions remain compared to Android.
Core NFC Framework Overview
Core NFC is in the CoreNFC framework (import CoreNFC). The two primary session classes are:
| Class | Purpose | iOS Version |
|---|---|---|
NFCNDEFReaderSession |
Read ndef-messages from any tag | 11+ |
NFCTagReaderSession |
Low-level access to specific tag tech (ISO 14443ISO 14443Standard for contactless smart cards at 13.56 MHz (Types A and B)View full →, 15693, FeliCa) | 13+ |
NFCNDEFTag protocol |
Read and write NDEF on discovered tags | 13+ |
Entitlement required: Add com.apple.developer.nfc.readersession.formats to your .entitlements file and configure the Privacy NFC Scan Usage Description in Info.plist.
<!-- Info.plist -->
<key>NFCReaderUsageDescription</key>
<string>Read NFC tags to verify product authenticity.</string>
NDEF Reading with NFCNDEFReaderSession
import CoreNFC
class NFCReader: NSObject, NFCNDEFReaderSessionDelegate {
var session: NFCNDEFReaderSession?
func beginScan() {
session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: false)
session?.alertMessage = "Hold your iPhone near an NFC tag."
session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
print("TNF: \(record.typeNameFormat.rawValue)")
print("Payload: \(record.payload)")
}
}
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
print("Session invalidated: \(error.localizedDescription)")
}
}
Tag Sessions and Writing
Writing requires NFCTagReaderSession (iOS 13+). The flow: detect tag → connect → cast to NFCNDEFTag → write message.
func readerSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
guard let tag = tags.first else { return }
session.connect(to: tag) { error in
guard error == nil else { session.invalidate(errorMessage: "Connection failed."); return }
if case let .miFare(mifareTag) = tag {
// handle MIFARE / NTAG via NFCMiFareTag
}
guard case let .iso7816(iso7816Tag) = tag else { return }
// send APDU commands for ISO 14443-4 tags
}
}
For nfc-tag types compliant with NFC Forum (NTAG, MIFARE Ultralight), use the NFCNDEFTag protocol's writeNDEF(_:completionHandler:) after connecting via NFCTagReaderSession.
Tag Type Support Matrix
| Tag Type | Protocol | iOS Support | Read | Write |
|---|---|---|---|---|
| Type 2 (nfc-a) | ISO 14443-3A | iOS 13+ | Yes | Yes (iOS 13+) |
| Type 4A (nfc-a) | ISO 14443-4A | iOS 13+ | Yes | Yes |
| Type 4B (nfc-b) | ISO 14443-4B | iOS 13+ | Yes | Limited |
| Type 3 (FeliCa) | JIS X 6319-4 | iOS 13+ | Yes | Yes |
| Type 5 (ISO 15693ISO 15693Standard for vicinity-range smart cards, 1+ meter read rangeView full →) | ISO 15693 | iOS 14+ | Yes | Yes |
Limitations and Platform Differences
| Feature | iOS | Android |
|---|---|---|
| Background tag reading | iOS 14+ (NDEF only) | Yes |
| HCE (card emulation) | No (NFC Express/Secure Element only) | Yes |
| NDEF write (app open) | iOS 13+ | API 10+ |
| P2P / Beam | No | Deprecated API 28 |
| reader-writer-mode without app open | iOS 14+ (URL tags only) | Yes (intent filters) |
The most significant limitation: iOS does not support card-emulation-mode via HCE. Payment and transit card emulation requires Apple's NFC Express feature or a hardware secure element under Apple's control.
Use the NDEF Message Encoder to build test payloads and the NDEF Message Decoder to inspect tag content during development.
See also: Android NFC Programming Guide and Web NFC API Guide.