Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Overflow reference for the core-nfc skill. Contains advanced patterns that exceed the main skill file's scope.
Send APDU commands to ISO 7816-compliant smart cards and tags:
import CoreNFC
func readISO7816(
tag: NFCISO7816Tag,
session: NFCTagReaderSession
) {
// Select application by AID
let selectAID = NFCISO7816APDU(
instructionClass: 0x00,
instructionCode: 0xA4,
p1Parameter: 0x04,
p2Parameter: 0x00,
data: Data([0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01]),
expectedResponseLength: -1
)
tag.sendCommand(apdu: selectAID) { data, sw1, sw2, error in
guard error == nil, sw1 == 0x90, sw2 == 0x00 else {
session.invalidate(errorMessage: "Select failed.")
return
}
// Read binary
let readBinary = NFCISO7816APDU(
instructionClass: 0x00,
instructionCode: 0xB0,
p1Parameter: 0x00,
p2Parameter: 0x00,
data: Data(),
expectedResponseLength: 256
)
tag.sendCommand(apdu: readBinary) { data, sw1, sw2, error in
guard error == nil else {
session.invalidate(errorMessage: "Read failed.")
return
}
print("Read \(data.count) bytes, SW: \(sw1) \(sw2)")
session.alertMessage = "Tag read successfully."
session.invalidate()
}
}
}| Command | CLA | INS | Description |
|---|---|---|---|
| SELECT | 0x00 | 0xA4 | Select an application or file |
| READ BINARY | 0x00 | 0xB0 | Read data from a transparent file |
| UPDATE BINARY | 0x00 | 0xD6 | Write data to a transparent file |
| READ RECORD | 0x00 | 0xB2 | Read a record from a record-oriented file |
| GET DATA | 0x00 | 0xCA | Retrieve a data object |
func readISO15693(
tag: NFCISO15693Tag,
session: NFCTagReaderSession
) {
// Read a single block
tag.readSingleBlock(
requestFlags: [.highDataRate, .address],
blockNumber: 0
) { data, error in
guard let data, error == nil else {
session.invalidate(errorMessage: "Read failed.")
return
}
print("Block 0: \(data.map { String(format: "%02x", $0) }.joined())")
}
// Read multiple blocks
tag.readMultipleBlocks(
requestFlags: [.highDataRate, .address],
blockRange: NSRange(location: 0, length: 4)
) { blocks, error in
guard let blocks, error == nil else { return }
for (index, block) in blocks.enumerated() {
print("Block \(index): \(block.count) bytes")
}
session.alertMessage = "Read \(blocks.count) blocks."
session.invalidate()
}
}tag.getSystemInfo(requestFlags: [.highDataRate, .address]) {
identifier, dsfid, afi, blockSize, blockCount, icReference, error in
guard error == nil else { return }
print("UID: \(identifier.map { String(format: "%02x", $0) }.joined())")
print("Block size: \(blockSize), Block count: \(blockCount)")
}func readMiFare(
tag: NFCMiFareTag,
session: NFCTagReaderSession
) {
// Identify MIFARE family
switch tag.mifareFamily {
case .ultralight:
readMiFareUltralight(tag: tag, session: session)
case .desfire:
readMiFareDESFire(tag: tag, session: session)
case .plus:
print("MIFARE Plus tag detected")
session.invalidate()
case .unknown:
print("Unknown MIFARE tag")
session.invalidate()
@unknown default:
session.invalidate()
}
}
func readMiFareUltralight(
tag: NFCMiFareTag,
session: NFCTagReaderSession
) {
// READ command: reads 4 pages starting at page 4
let readCommand = Data([0x30, 0x04])
tag.sendMiFareCommand(commandPacket: readCommand) { data, error in
guard error == nil else {
session.invalidate(errorMessage: "Read failed.")
return
}
print("Read \(data.count) bytes from MIFARE Ultralight")
session.alertMessage = "Tag read successfully."
session.invalidate()
}
}Build messages with multiple records of different types:
func buildMultiRecordMessage() -> NFCNDEFMessage {
var records: [NFCNDEFPayload] = []
// Text record
if let textPayload = NFCNDEFPayload.wellKnownTypeTextPayload(
string: "Product: Widget Pro",
locale: Locale(identifier: "en")
) {
records.append(textPayload)
}
// URL record
if let urlPayload = NFCNDEFPayload.wellKnownTypeURIPayload(
url: URL(string: "https://example.com/product/123")!
) {
records.append(urlPayload)
}
// Custom external type record
let externalPayload = NFCNDEFPayload(
format: .nfcExternal,
type: "com.example:product".data(using: .utf8)!,
identifier: Data(),
payload: """
{"sku":"WP-001","batch":"2026-03"}
""".data(using: .utf8)!
)
records.append(externalPayload)
return NFCNDEFMessage(records: records)
}func writeIfFits(
message: NFCNDEFMessage,
to tag: any NFCNDEFTag,
session: NFCNDEFReaderSession
) {
tag.queryNDEFStatus { status, capacity, error in
guard status == .readWrite else {
session.invalidate(errorMessage: "Tag is not writable.")
return
}
let messageLength = message.length
guard messageLength <= capacity else {
session.invalidate(
errorMessage: "Message (\(messageLength) bytes) exceeds "
+ "tag capacity (\(capacity) bytes)."
)
return
}
tag.writeNDEF(message) { error in
if let error {
session.invalidate(
errorMessage: "Write failed: \(error.localizedDescription)"
)
} else {
session.alertMessage = "Written \(messageLength) bytes."
session.invalidate()
}
}
}
}Lock a tag to make it permanently read-only. This is irreversible.
func lockTag(
_ tag: any NFCNDEFTag,
session: NFCNDEFReaderSession
) {
tag.queryNDEFStatus { status, _, error in
guard status == .readWrite else {
session.invalidate(errorMessage: "Tag is already read-only.")
return
}
tag.writeLock { error in
if let error {
session.invalidate(
errorMessage: "Lock failed: \(error.localizedDescription)"
)
} else {
session.alertMessage = "Tag locked permanently."
session.invalidate()
}
}
}
}Wrap the NFC reader in an @Observable model for SwiftUI integration:
import CoreNFC
import SwiftUI
@Observable
@MainActor
final class NFCScannerModel: NSObject {
var scannedText: String = ""
var scannedURL: URL?
var isScanning = false
var errorMessage: String?
private var session: NFCNDEFReaderSession?
var isAvailable: Bool {
NFCNDEFReaderSession.readingAvailable
}
func startScan() {
guard isAvailable else {
errorMessage = "NFC not available on this device."
return
}
session = NFCNDEFReaderSession(
delegate: self,
queue: nil,
invalidateAfterFirstRead: true
)
session?.alertMessage = "Hold your iPhone near an NFC tag."
session?.begin()
isScanning = true
errorMessage = nil
}
}
extension NFCScannerModel: NFCNDEFReaderSessionDelegate {
nonisolated func readerSessionDidBecomeActive(
_ session: NFCNDEFReaderSession
) { }
nonisolated func readerSession(
_ session: NFCNDEFReaderSession,
didDetectNDEFs messages: [NFCNDEFMessage]
) {
Task { @MainActor in
for message in messages {
for record in message.records {
if let url = record.wellKnownTypeURIPayload() {
scannedURL = url
}
if let (text, _) = record.wellKnownTypeTextPayload() {
scannedText = text
}
}
}
isScanning = false
}
}
nonisolated func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
Task { @MainActor in
let nfcError = error as? NFCReaderError
if nfcError?.code != .readerSessionInvalidationErrorUserCanceled,
nfcError?.code != .readerSessionInvalidationErrorFirstNDEFTagRead {
errorMessage = error.localizedDescription
}
isScanning = false
self.session = nil
}
}
}
struct NFCScannerView: View {
@State private var scanner = NFCScannerModel()
var body: some View {
VStack {
if !scanner.isAvailable {
ContentUnavailableView(
"NFC Unavailable",
systemImage: "wave.3.right.circle.fill",
description: Text("This device does not support NFC.")
)
} else {
Button("Scan NFC Tag") {
scanner.startScan()
}
.buttonStyle(.borderedProminent)
.disabled(scanner.isScanning)
if let url = scanner.scannedURL {
Text("URL: \(url.absoluteString)")
}
if !scanner.scannedText.isEmpty {
Text("Text: \(scanner.scannedText)")
}
if let error = scanner.errorMessage {
Text(error).foregroundStyle(.red)
}
}
}
.padding()
}
}| Code | Meaning |
|---|---|
.readerSessionInvalidationErrorUserCanceled | User tapped Cancel in the NFC sheet |
.readerSessionInvalidationErrorFirstNDEFTagRead | Session ended after first read (when invalidateAfterFirstRead is true) |
.readerSessionInvalidationErrorSessionTimeout | 60-second session timeout elapsed |
.readerSessionInvalidationErrorSessionTerminatedUnexpectedly | System terminated the session |
.readerTransceiveErrorTagConnectionLost | Tag moved out of range during communication |
.readerTransceiveErrorRetryExceeded | Too many failed communication attempts |
.readerTransceiveErrorTagNotConnected | Attempted to communicate without connecting first |
.readerSessionInvalidationErrorSystemIsBusy | Another NFC session is active |
nonisolated func readerSession(
_ session: NFCNDEFReaderSession,
didInvalidateWithError error: Error
) {
let nfcError = error as? NFCReaderError
Task { @MainActor in
switch nfcError?.code {
case .readerSessionInvalidationErrorUserCanceled:
break // User chose to cancel
case .readerSessionInvalidationErrorFirstNDEFTagRead:
break // Expected when invalidateAfterFirstRead is true
case .readerSessionInvalidationErrorSessionTimeout:
errorMessage = "Scan timed out. Try again."
case .readerTransceiveErrorTagConnectionLost:
errorMessage = "Tag moved away. Hold steady and try again."
default:
errorMessage = error.localizedDescription
}
self.session = nil
}
}skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
references
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-localization
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
swift-charts
references
swift-codable
swift-concurrency
swift-formatstyle
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references