core-nfc
Read and write NFC tags using CoreNFC. Use when scanning NDEF tags, reading ISO7816/ISO15693/FeliCa/MIFARE tags, writing NDEF messages, handling NFC session lifecycle, configuring NFC entitlements, or implementing background tag reading in iOS apps.
What this skill does
# CoreNFC
Read and write NFC tags on iPhone using the CoreNFC framework. Covers NDEF
reader sessions, tag reader sessions, NDEF message construction, entitlements,
and background tag reading. Targets Swift 6.3 / iOS 26+.
## Contents
- [Setup](#setup)
- [NDEF Reader Session](#ndef-reader-session)
- [Tag Reader Session](#tag-reader-session)
- [Writing NDEF Messages](#writing-ndef-messages)
- [NDEF Payload Types](#ndef-payload-types)
- [Background Tag Reading](#background-tag-reading)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
## Setup
### Project Configuration
1. Add the **Near Field Communication Tag Reading** capability in Xcode
2. Add `NFCReaderUsageDescription` to Info.plist with a user-facing reason string
3. Add the `com.apple.developer.nfc.readersession.formats` entitlement with the current `TAG` value; do not add legacy `NDEF`
4. For ISO 7816 tags, add supported application identifiers to `com.apple.developer.nfc.readersession.iso7816.select-identifiers` in Info.plist
5. For FeliCa tags, add supported system codes to `com.apple.developer.nfc.readersession.felica.systemcodes`; do not use wildcard system codes
### Device Requirements
NFC reading requires iPhone 7 or later. Always check for reader session
availability before creating NFC UI or sessions. Use the concrete reader
session type you are about to create.
```swift
import CoreNFC
guard NFCNDEFReaderSession.readingAvailable else {
// Device does not support NFC or feature is restricted
showUnsupportedMessage()
return
}
```
### Key Types
| Type | Role |
|---|---|
| `NFCNDEFReaderSession` | Scans for NDEF-formatted tags |
| `NFCTagReaderSession` | Scans for ISO7816, ISO15693, FeliCa, MIFARE tags |
| `NFCNDEFMessage` | Collection of NDEF payload records |
| `NFCNDEFPayload` | Single record within an NDEF message |
| `NFCNDEFTag` | Protocol for interacting with an NDEF-capable tag |
## NDEF Reader Session
Use `NFCNDEFReaderSession` to read NDEF-formatted data from tags. This is the
simplest path for reading standard tag content like URLs, text, and MIME data.
```swift
import CoreNFC
final class NDEFReader: NSObject, NFCNDEFReaderSessionDelegate {
private var session: NFCNDEFReaderSession?
func beginScanning() {
guard NFCNDEFReaderSession.readingAvailable else { return }
session = NFCNDEFReaderSession(
delegate: self,
queue: nil,
invalidateAfterFirstRead: false
)
session?.alertMessage = "Hold your iPhone near an NFC tag."
session?.begin()
}
// MARK: - NFCNDEFReaderSessionDelegate
func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
// Session is scanning
}
func readerSession(
_ session: NFCNDEFReaderSession,
didDetectNDEFs messages: [NFCNDEFMessage]
) {
for message in messages {
for record in message.records {
processRecord(record)
}
}
}
func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
let nfcError = error as? NFCReaderError
if nfcError?.code != .readerSessionInvalidationErrorFirstNDEFTagRead,
nfcError?.code != .readerSessionInvalidationErrorUserCanceled {
print("Session invalidated: \(error.localizedDescription)")
}
self.session = nil
}
}
```
### Reading with Tag Connection
For read-write operations, use the tag-detection delegate method to connect
to individual tags:
```swift
func readerSession(
_ session: NFCNDEFReaderSession,
didDetect tags: [any NFCNDEFTag]
) {
guard let tag = tags.first else {
session.restartPolling()
return
}
session.connect(to: tag) { error in
if let error {
session.invalidate(errorMessage: "Connection failed: \(error)")
return
}
tag.queryNDEFStatus { status, capacity, error in
guard error == nil else {
session.invalidate(errorMessage: "Query failed.")
return
}
switch status {
case .notSupported:
session.invalidate(errorMessage: "Tag is not NDEF compliant.")
case .readOnly:
tag.readNDEF { message, error in
if let message {
self.processMessage(message)
}
session.invalidate()
}
case .readWrite:
tag.readNDEF { message, error in
if let message {
self.processMessage(message)
}
session.alertMessage = "Tag read successfully."
session.invalidate()
}
@unknown default:
session.invalidate()
}
}
}
}
```
## Tag Reader Session
Use `NFCTagReaderSession` when you need direct access to the native tag
protocol (ISO 7816, ISO 15693, FeliCa, or MIFARE).
Polling options are protocol-specific: `.iso14443` detects ISO
7816-compatible and MIFARE tags, `.iso15693` detects ISO 15693 tags, and
`.iso18092` detects FeliCa tags. Do not use `NFCTagReaderSession` for
payment-related AIDs; Apple documents `NFCPaymentTagReaderSession` for
eligible EU payment use cases.
```swift
final class TagReader: NSObject, NFCTagReaderSessionDelegate {
private var session: NFCTagReaderSession?
func beginScanning() {
guard NFCTagReaderSession.readingAvailable else { return }
session = NFCTagReaderSession(
pollingOption: [.iso14443, .iso15693, .iso18092],
delegate: self,
queue: nil
)
session?.alertMessage = "Hold your iPhone near a tag."
session?.begin()
}
func tagReaderSessionDidBecomeActive(
_ session: NFCTagReaderSession
) { }
func tagReaderSession(
_ 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
}
switch tag {
case .iso7816(let iso7816Tag):
self.readISO7816(tag: iso7816Tag, session: session)
case .miFare(let miFareTag):
self.readMiFare(tag: miFareTag, session: session)
case .iso15693(let iso15693Tag):
self.readISO15693(tag: iso15693Tag, session: session)
case .feliCa(let feliCaTag):
self.readFeliCa(tag: feliCaTag, session: session)
@unknown default:
session.invalidate(errorMessage: "Unsupported tag type.")
}
}
}
func tagReaderSession(
_ session: NFCTagReaderSession,
didInvalidateWithError error: Error
) {
self.session = nil
}
}
```
## Writing NDEF Messages
Write NDEF data to a connected tag. Always check `readWrite` status first.
```swift
func writeToTag(
tag: any NFCNDEFTag,
session: NFCNDEFReaderSession,
url: URL
) {
tag.queryNDEFStatus { status, capacity, error in
guard status == .readWrite else {
session.invalidate(errorMessage: "Tag is read-only.")
return
}
guard let payload = NFCNDEFPayload.wellKnownTypeURIPayload(
url: url
) else {
session.invalidate(errorMessage: "Invalid URL.")
return
}
let message = NFCNDEFMessage(records: [payload])
tag.writeNDEF(message) { error in
if let error {
session.invalidate(
errorMessage: "Write failed: \(error.localizedDescription)"
Related in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.