«Три крапки» в месенджері — один із тих дрібних елементів UX, які користувачі помічають тільки коли його немає. Середня затримка індикатора при правильній реалізації — близько 100 мс, але без оптимізації вона може перевищувати 500 мс, викликаючи роздратування. Реалізація здається тривіальною, поки не впираєшся в дебаунс, консистентність між пристроями та правильну роботу в умовах нестабільного з'єднання. Ми розробили механізм, який працює на iOS, Android та Flutter, і готові адаптувати його під ваш проєкт. Звертайтеся — отримаєте детальний план впровадження за один день.
Як реалізувати індикатор набору тексту в чаті?
Базова схема: клієнт надсилає подію typing_start при кожному натисканні клавіші, співрозмовник бачить індикатор, через N секунд без нових подій індикатор гасне. На папері просто. На практиці — три типові проблеми.
Чому дебаунс критично важливий? — реалізація індикатора набору
Флуд подій. Без дебаунсу кожен onTextChanged (Android) або textDidChange (iOS) генерує мережевий виклик. Користувач, який пише повідомлення за 5 секунд, створює 20–30 зайвих запитів. Правильне рішення — надсилати typing_start тільки якщо минуло більше 2–3 секунд з моменту попереднього надсилання, і typing_stop із затримкою ~4 секунди після останнього символу.
На Android це реалізується через Handler.postDelayed або debounce оператор в Kotlin Flow:
private var typingJob: Job? = null fun onTextChanged(text: String) { if (text.isEmpty()) { sendTypingStop() return } typingJob?.cancel() typingJob = viewModelScope.launch { delay(TYPING_THROTTLE_MS) // 3000ms sendTypingStart() } } На iOS аналогічна логіка через Timer.scheduledTimer або Combine:
private var typingCancellable: AnyCancellable? func textDidChange(_ text: String) { typingCancellable?.cancel() typingCancellable = Just(text) .delay(for: .seconds(3), scheduler: RunLoop.main) .sink { [weak self] _ in self?.sendTypingStart() } } Як вибрати транспорт для typing events?
Транспортний шар. Індикатор набору — це ефемерний стан, який не потребує гарантій доставки. Тому REST/HTTP тут надлишковий. Оптимальний вибір — WebSocket або Firebase Realtime Database. Через WebSocket надсилаємо легкий JSON-пакет {"type":"typing","chat_id":"...","user_id":"..."}. Через Firebase — запис в ефемерний вузол /typing/{chatId}/{userId} з TTL через onDisconnect().removeValue(). Другий варіант автоматично чистить стан при розриві з'єднання — що критично.
Порівняння підходів:
| Критерій | WebSocket | Firebase RTDB |
|---|---|---|
| Затримка | 2–5 мс | 20–50 мс |
| Автоочистка при обриві | Потрібно реалізувати heartbeat | Вбудована (onDisconnect) |
| Складність інтеграції | Середня | Низька |
| Навантаження на сервер | Висока (постійне з'єднання) | Низька (керується Firebase) |
Як анімувати індикатор на різних платформах?
Анімація трьох крапок: на iOS — кастомний CABasicAnimation або Lottie, на Android — AnimationDrawable або LottieAnimationView. Flutter вирішує це через AnimatedOpacity + Timer. Важливо: анімація має бути легкою і не витрачати CPU при тривалому показі — використовуйте паузу через таймер.
Порівняння варіантів анімації:
| Платформа | Підхід | Продуктивність | Складність |
|---|---|---|---|
| iOS | CABasicAnimation | 60 FPS | Низька |
| Android | LottieAnimationView | 60 FPS | Середня |
| Flutter | AnimatedOpacity + Transform | 60 FPS | Середня |
Відображення на приймачі
Після отримання події потрібно показати індикатор і запустити таймер очищення (~5 секунд). Якщо за цей час прийде нова подія — таймер скидається. Типова помилка — забути оновити таймер, через що індикатор гасне раніше часу. Рекомендуємо використовувати єдиний таймер у ViewModel, який скасовується і перезапускається при кожній новій події. Переконайтеся, що додаток коректно обробляє сценарій втрати з'єднання: при відновленні необхідно повторно підписатися на події чату.
Як уникнути проблем з модерацією App Store?
Apple суворо перевіряє використання фонових режимів і push-сповіщень. Якщо індикатор набору потребує постійного з'єднання — вкажіть у додатку фоновий режим "Voice over IP" або використовуйте WebSocket з NSURLSessionWebSocketTask. Для обходу вимог розділу 4.2 та 5.1 краще додати чекбокс згоди користувача. Ми гарантуємо проходження рев'ю з першого разу — наші інженери врахують всі нюанси.
Що включає наша робота з впровадження?
- Аудит поточного транспортного шару: WebSocket, Firebase, XMPP — оцінюємо продуктивність і надійність.
- Розробка дебаунс-логіки під потрібну платформу з тестуванням на граничні випадки (швидке введення, видалення тексту, втрата з'єднання).
- Реалізація анімації індикатора з урахуванням гайдлайнів платформи.
- Інтеграція з серверною частиною: доопрацювання API, налаштування Firebase, конфігурація WebSocket.
- Тестування на реальних пристроях та Simulator/Emulator.
- Надання документації та готового модуля для вбудовування в проєкт.
Термін виконання: від 1 до 3 днів залежно від складності поточної архітектури чату. Замовте впровадження — отримайте робочий прототип у найкоротші терміни.
Наш досвід
Ми працюємо з мобільними додатками більше 5 років і реалізували чати для 15+ проєктів із сумарною аудиторією понад 500 000 користувачів. Знаємо типові підводні камені App Store Review і гарантуємо проходження рев'ю з першого разу.
Щоб отримати консультацію або замовити впровадження індикатора набору тексту у ваш чат, зв'яжіться з нами — оцінимо проєкт і запропонуємо рішення під ваш стек. Отримайте детальний план впровадження за один день.







