«Три точки» в мессенджере — один из тех мелких элементов 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 и гарантируем прохождение ревью с первого раза.
Чтобы получить консультацию или заказать внедрение индикатора набора текста в ваш чат, свяжитесь с нами — оценим проект и предложим решение под ваш стек. Получите детальный план внедрения за один день.







