Інтеграція NFC у мобільне додаток
NFC у мобільному додатку використовується для трьох завдань: читання тегів (паспорти, транспортні карти, NFC-метки), запис на перезаписувані теги, та peer-to-peer обмін з іншим пристроєм. Платформи реалізують ці режими по-різному, та iOS історично обмежувала можливості значно більше ніж Android.
iOS: Core NFC та його обмеження
iOS підтримує NFC з iPhone 7 (iOS 11), але повнофункціональне читання NDEF та non-NDEF тегів з'явилося тільки в iOS 13. Запис на теги — iOS 13+. Background tag reading (без явного запуску сесії) — iOS 14+.
Ключові класи: NFCNDEFReaderSession — для NDEF-тегів, NFCTagReaderSession — для non-NDEF (ISO 7816, ISO 15693, FeliCa, MIFARE).
import CoreNFC
class NFCManager: NSObject, NFCNDEFReaderSessionDelegate {
var session: NFCNDEFReaderSession?
func startReading() {
guard NFCNDEFReaderSession.readingAvailable else {
// пристрій не підтримує NFC або NFC відключено
return
}
session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
session?.alertMessage = "Поднесіть NFC-метку"
session?.begin()
}
func readerSession(_ session: NFCNDEFReaderSession,
didDetectNDEFs messages: [NFCNDEFMessage]) {
for message in messages {
for record in message.records {
if record.typeNameFormat == .nfcWellKnown,
let type = String(data: record.type, encoding: .utf8),
type == "T" {
// текстова запис
let payload = record.payload
// перший байт — encoding + lang length, парсимо вручну
}
}
}
}
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// NFCReaderError.readerSessionInvalidationErrorUserCanceled — користувач закрив
// NFCReaderError.readerSessionInvalidationErrorSessionTimeout — таймаут
}
}
Запис на теги
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
guard let tag = tags.first else { return }
session.connect(to: tag) { error in
guard error == nil else { return }
tag.queryNDEFStatus { status, capacity, error in
guard status == .readWrite else {
session.invalidate(errorMessage: "Тег захищений від запису")
return
}
let payload = NFCNDEFPayload.wellKnownTypeURIPayload(url: URL(string: "https://example.com")!)!
let message = NFCNDEFMessage(records: [payload])
tag.writeNDEF(message) { error in
session.invalidate(errorMessage: error != nil ? "Помилка запису" : nil)
}
}
}
}
Для роботи з non-NDEF тегами (банківські карти ISO 7816, паспорти) потрібен NFCTagReaderSession з pollingOption: [.iso14443]. Читання паспортів (ePassport) — через PACE або Basic Access Control з ключами з MRZ рядка документа.
Android: NfcAdapter та foreground dispatch
Android NFC API більш відкритий. NfcAdapter.getDefaultAdapter(context) — перевіряємо наявність NFC. enableForegroundDispatch — перехоплюємо теги поки додаток на переднему плані.
val nfcAdapter = NfcAdapter.getDefaultAdapter(context)
// У onResume
val intent = Intent(context, activity.javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_MUTABLE)
nfcAdapter.enableForegroundDispatch(activity, pendingIntent, null, null)
// У onNewIntent
fun handleNfcIntent(intent: Intent) {
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG) ?: return
val ndef = Ndef.get(tag)
if (ndef != null) {
ndef.connect()
val message = ndef.ndefMessage
for (record in message.records) {
val payload = String(record.payload, Charsets.UTF_8)
}
ndef.close()
}
}
Для MIFARE Classic — окремий клас MifareClassic, вимагає аутентифікації ключем перед читанням сектора. Ключ A за замовчуванням: {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}. Більшість транспортних карт використовують кастомні ключі.
Типові проблеми
Таймаут сесії на iOS. NFCNDEFReaderSession живе 60 секунд. Якщо користувач не встигнув поднести тег — сесія закривається з помилкою. Потрібно показувати чітку інструкцію та пропонувати повторити.
Конфлікт з платіжними додатками на Android. Якщо встановлено Google Pay й налаштовано як NFC-додаток за замовчуванням, enableForegroundDispatch перехоплює теги тільки поки додаток активний. У фоні — керує Google Pay.
NFC не працює в чохлі. Не жартую — деякі металеві чохли екранують антену. Це потрібно описувати в вимогах до використання.
Терміни інтеграції: 2-3 дні — читання NDEF; 4-7 днів — якщо потрібна запис, non-NDEF або робота з захищеними тегами. Вартість розраховується індивідуально.







