Реалізація читання/запису NFC-меток через мобільне приложення
NFC-метки — це не про «тапнув і працює». На практиці в 30–40% випадків перший запит до метки повертає NFCReaderError.readerTransceiveErrorTagConnectionLost на iOS або IOException: Tag lost на Android раніше, ніж встигне прочитати навіть заголовок NDEF-запису. Причина — занадто коротке утримання телефону над меткою, перешкоди від металевих поверхонь або неправильний тайм-аут сесії.
Де частіше ломається інтеграція
iOS: обмеження CoreNFC та типи тегів
Apple додала CoreNFC в iOS 11, але повнофункціональний read/write NDEF з'явився тільки в iOS 13. До сих пір зустрічаються проекти, які таргетують iOS 12 і не можуть пояснити, чому у половини користувачів метки «не читаються».
Другий камінь — NFCNDEFReaderSession проти NFCTagReaderSession. Перший тип сесії працює тільки з NDEF-сумісними метками. Якщо клієнт принесе Mifare Classic — він просто не запрацює: Apple не підтримує цей протокол з міркувань безпеки. Потрібно заздалегідь перевірити тип метки та використовувати NFCTagReaderSession з NFCMiFareTag для Mifare Ultralight або NFCISO7816Tag для смарт-карт з APDU-командами.
Ще болючое місце — Background Tag Reading, доступний з iOS 13 на iPhone XS/XR та новіше. Приложення не запущено, користувач підносить телефон до метки — система читає 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_DISCOVERED, то метка формату NDEF буде перехоплена коректно, але «сира» метка без NDEF-структури піде в інше приложення або взагалі не прийде.
Foreground dispatch через NfcAdapter.enableForegroundDispatch() вирішує це, але потребує чіткого життєвого циклу: включати в onResume, вимикати в onPause. Один пропущений disableForegroundDispatch — і приложення починає отримувати NFC-інтенти навіть коли воно неактивне, що ламає UX.
Для запису NDEF на Android: якщо метка write-protected або відформатована під інший тип — tag.connect() підвиснеутьсяя або выбросит IOException. Потрібен явний тайм-аут через tag.setTimeout() для конкретного tech-класу та retry-логіка з експоненціальним backoff.
Як ми реалізуємо
iOS — стек та підхід
Працюємо з CoreNFC через NFCNDEFReaderSession для стандартних сценаріїв та NFCTagReaderSession для нестандартних форматів. Для запису — створюємо NFCNDEFMessage з потрібними NFCNDEFPayload, використовуємо NFCNDEFPayload.wellKnownTypeURIPayload() для URI-записів замість ручного кодування байт, що виключає помилки TNF-заголовків.
Сесію оборачиваємо в async/await через Continuation, щоб не тянутиDelegate-ланцюжок через весь ViewModel. Помилки мепіємо на зрозумілі користувачу стани — sessionTimeout, tagNotCompatible, writeProtected — і показуємо через нативний NFCReaderSession alert або власний UI.
Android — стек та підхід
Використовуємо Ndef та NdefFormatable tech-класи через Tag.getTechList(). Перед записом перевіряємо ndef.isWritable() та ndef.maxSize() — типова помилка: намагатися записати 500 байт на 144-байтну Ntag213. Для форматування чистих меток використовуємо NdefFormatable.format() з мінімальним початковим повідомленням.
Весь NFC-код виносимо в NfcRepository з Flow<NfcEvent>, UI-слой підписується через collectLatest. При зміні конфігурації (поворот екрана) сесія не перериває — Activity переоткривається, а foreground dispatch відновлюється в onResume.
Підтримувані формати меток
| Тип метки | iOS | Android | Примітки |
|---|---|---|---|
| NDEF (Ntag213/215/216) | ✓ | ✓ | Найпоширеніші |
| Mifare Ultralight | ✓ (ISO7816) | ✓ | Потребує NFCTagReaderSession на iOS |
| Mifare Classic | ✗ | ✓ | Apple не підтримує |
| ISO 15693 | ✓ (iOS 14+) | ✓ | Для промислових меток |
| FeliCa | ✓ (тільки Японія) | ✓ | Транспортні карти |
Процес роботи
Починаємо з аудиту: які типи меток використовуються, який обсяг даних записується, потрібен ли background reading, потрібна ли запись або тільки читання. Від цього залежить вибір tech-класів та прав в entitlements/manifest.
Далі — розробка з unit-тестами на mock-тегах через NFCNDEFReaderSessionMock (iOS) та MockNdefTag (Android). Фінальне тестування — на реальних метках різних виробників: NXP, Broadcom, ST Microelectronics ведуть себе трохи по-різному навіть у рамках одного стандарту.
Терміни
Базова інтеграція чтення/запису NDEF на одній платформі — 3–5 робочих днів. Якщо потрібні нестандартні формати, background reading та підтримка кількох типів меток — від 2 тижнів. Оцінка після аналізу вимог.







