Реализация чтения/записи NFC-меток через мобильное приложение
Мы часто сталкиваемся с ситуацией, когда 30–40% первых запросов к NFC-метке возвращают NFCReaderError.readerTransceiveErrorTagConnectionLost на iOS или IOException: Tag lost на Android. Причина — слишком короткое удержание телефона, interference от металлических поверхностей или неправильный тайм-аут сессии. Без системного подхода интеграция превращается в бесконечную отладку.
Почему возникают ошибки чтения NFC?
iOS: ограничения CoreNFC
CoreNFC появился сравнительно давно, но полноценное чтение и запись NDEF стало доступно только в более поздних версиях iOS. Если проект таргетирует старые версии, половина пользователей останется без NFC. Второй камень — NFCNDEFReaderSession против NFCTagReaderSession. Первый работает только с NDEF-совместимыми метками. Если клиент принёс Mifare Classic, он просто не заработает: Apple не поддерживает этот протокол из соображений безопасности. Нужно заранее проверить тип метки и использовать NFCTagReaderSession с NFCMiFareTag для Mifare Ultralight или NFCISO7816Tag для смарт-карт.
Ещё одно узкое место — Background Tag Reading. Приложение не запущено, пользователь подносит телефон к метке — система читает NDEF-URI и запускает приложение. Выглядит магически, но требует com.apple.developer.nfc.readersession.formats в entitlements с NDEF, плюс URL-схему или Universal Link. Если забыть добавить домен в apple-app-site-association, deep link просто не откроется.
Android: фрагментация NFC-стека
На Android NfcAdapter.ACTION_NDEF_DISCOVERED, ACTION_TAG_DISCOVERED, ACTION_TECH_DISCOVERED — три разных интента с разным приоритетом. Если объявить только NDEF, то метка без NDEF-структуры уйдёт в другое приложение. Foreground dispatch через enableForegroundDispatch() решает это, но требует чёткого жизненного цикла: включать в onResume, отключать в onPause. Один пропущенный вызов — и приложение начинает получать NFC-интенты даже когда не активно.
Для записи NDEF: если метка write-protected или отформатирована под другой тип, tag.connect() подвиснет или выбросит IOException. Нужен явный тайм-аут через tag.setTimeout() и retry-логика с экспоненциальным backoff. Наш опыт показывает, что грамотная обработка ошибок сокращает количество сбоев в 3 раза.
Как обеспечить стабильное чтение NFC на обеих платформах?
iOS
Работаем с CoreNFC через NFCNDEFReaderSession для стандартных сценариев и NFCTagReaderSession для нестандартных форматов. Для записи создаём NFCNDEFMessage с нужными NFCNDEFPayload. Используем NFCNDEFPayload.wellKnownTypeURIPayload() для URI-записей — это исключает ошибки TNF-заголовков. Сессию оборачиваем в async/await через Continuation, чтобы не тянуть delegate-цепочку. Ошибки маппим на понятные состояния — sessionTimeout, tagNotCompatible, writeProtected — и показываем через нативный alert или собственный UI. Среднее время чтения метки — 0.3 секунды, 98% сессий завершаются успешно при правильном удержании.
Android
Используем Ndef и NdefFormatable tech-классы через Tag.getTechList(). Перед записью проверяем ndef.isWritable() и ndef.maxSize() — типичная ошибка: пытаться записать 500 байт на 144-байтную Ntag213. Для форматирования чистых меток используем NdefFormatable.format() с минимальным начальным сообщением.
Весь NFC-код выносим в NfcRepository с Flow<NfcEvent>, UI-слой подписывается через collectLatest. При повороте экрана сессия не прерывается — foreground dispatch восстанавливается в onResume. Такой подход опробован на 15+ проектах за 5 лет работы. Гарантируем стабильность сессии даже на сложных конфигурациях.
Поддерживаемые форматы меток
| Тип метки | iOS | Android | Примечания |
|---|---|---|---|
| NDEF (Ntag213/215/216) | ✓ | ✓ | Наиболее распространённые |
| Mifare Ultralight | ✓ (ISO7816) | ✓ | Требует NFCTagReaderSession на iOS |
| Mifare Classic | ✗ | ✓ | Apple не поддерживает |
| ISO 15693 | ✓ (iOS 14+) | ✓ | Для промышленных меток |
| FeliCa | ✓ (только Япония) | ✓ | Транспортные карты |
Сравнение iOS и Android
| Аспект | iOS | Android |
|---|---|---|
| Стабильность сессии | Высокая, но жёсткие ограничения по типам | Ниже из-за фрагментации, но гибче |
| Поддержка типов меток | Ограничена (без Mifare Classic) | Широкая, включая все популярные |
| Background reading | iOS 13+, требует entitlements | Android 10+, Beam устарел |
| Сложность конфигурации | Выше (entitlements, provisioning) | Ниже (манифест + dispatch) |
Типичные ошибки и как их избежать
- Tag Lost на iOS: увеличить тайм-аут, добавить retry, информировать пользователя.
- Write защищённой метки: проверять
isWritable()и уведомлять. - Прерывание сессии при повороте экрана Android: вынос логики в репозиторий с Flow.
- Несовместимость типов меток: проводить аудит перед разработкой.
Что входит в работу
- Аудит: определяем типы меток, объём данных, нужен ли background reading, платформы. Учитываем коммерческую экономию бюджета до 20% за счёт правильного выбора стека.
- Проектирование: выбор tech-классов, архитектура (репозиторий с Flow/async/await).
- Реализация: unit-тесты на mock-тегах (NFCNDEFReaderSessionMock на iOS, MockNdefTag на Android).
- Тестирование на реальных метках NXP, Broadcom, ST Microelectronics.
- Документация по интеграции и инструкция для пользователей.
- Сопровождение в течение 30 дней после сдачи.
Получите консультацию по вашему проекту — оценим объём и сроки. Закажите аудит NFC-интеграции: мы проанализируем ваши метки, платформы и сценарии использования.
Процесс работы
- Изучение — анализ требований, типов меток, условий использования.
- Проектирование — выбор стека, создание прототипа сессии.
- Разработка — реализация с тестами на эмуляторах и реальных устройствах.
- Тестирование — на 10+ образцах меток разных производителей.
- Запуск — публикация в App Store / Google Play, настройка background reading.
Сроки и стоимость
Базовая интеграция чтения/записи NDEF на одной платформе занимает 3–5 рабочих дней. Если нужны нестандартные форматы, background reading или поддержка нескольких типов меток — от 2 недель. Точную оценку даём после аудита.
Ссылки:







