iOS Core NFC Programming Guide

Reading and Writing NFC Tags in Swift

| 3 min read

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 Forum 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 14443, 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 15693) 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.

Terms in This Guide