Интеграция 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 или работа с защищёнными тегами. Стоимость рассчитывается индивидуально.







