Разработка чата в мобильном приложении (один на один)
Приватный чат между двумя пользователями — не «просто список сообщений». Здесь проблема обычно не в отображении, а в синхронизации состояний: пользователь на iOS отправил сообщение, Android-собеседник его видит через 3 секунды с дублем, потому что WebSocket отвалился и клиент переотправил через REST. Или история не загружается при плохом соединении, потому что пагинация реализована через OFFSET и timeout бьёт по 500-й странице.
Мы в TrueTech решаем эти проблемы с 5-летним опытом. За это время мы реализовали 40+ проектов с чатами, включая мессенджеры для EdTech и FinTech. Гарантируем стабильную работу даже при агрессивной экономии трафика. Свяжитесь с нами, чтобы обсудить ваш проект — мы оценим задачу за 1 день.
Какой транспорт выбрать для real-time чата?
Для чата один на один достаточно WebSocket-соединения. На iOS — URLSessionWebSocketTask (нативно, без зависимостей) или Starscream если нужна более гибкая обработка heartbeat. На Android — OkHttp WebSocket из коробки через Retrofit-экосистему или Ktor WebSocket Client для Kotlin Multiplatform. Как указано в документации Apple, эти библиотеки поддерживают TLS 1.3, что гарантирует безопасность транспорта.
Критичный момент — реконнект. WebSocket рвётся при смене сети (Wi-Fi → 4G), при блокировке фона iOS, при агрессивном Doze Mode на Android. Нужен exponential backoff: первый реконнект через 1s, затем 2s, 4s, 8s, cap 30s. После реконнекта — запрос missed messages с last_message_id чтобы не пропустить то, что пришло пока соединение было разорвано. Мы гарантируем, что сообщения не теряются благодаря идемпотентности и replay-механизму.
Firebase Realtime Database или Firestore — альтернатива собственному WS-серверу для небольших проектов. Быстро стартовать, но при сложной бизнес-логике (модерация, шифрование на уровне сервера) возникает ограничение Cloud Functions.
Что входит в разработку чата под ключ?
| Компонент | Описание | Срок |
|---|---|---|
| WebSocket-сервер | Настройка WS, реконнект, heartbeat, missed messages | 1-2 дня |
| Модель данных | Проектирование схемы сообщений и диалогов, миграции | 1 день |
| UI чата | Список сообщений, индикаторы набора, статусы доставки | 2-3 дня |
| Пагинация | cursor-based, инвертированный список, плавная подгрузка | 1 день |
| Push-уведомления | APNs/FCM, расшифровка превью, deep link | 1-2 дня |
| E2E-шифрование | Signal Protocol, управление ключами (опционально) | 2-3 дня |
Базовый набор включает все пункты, кроме E2E. Мы также предоставляем документацию API, инструкции по развертыванию и 2 недели поддержки после запуска. Закажите разработку чата под ключ – получите консультацию инженера бесплатно.
Как мы строим чат: архитектура и ключевые решения
Модель данных
Сообщение: id, conversation_id, sender_id, body, type (text/image/file/system), status (sent/delivered/read), client_message_id (UUID генерируется на клиенте), created_at. client_message_id — ключ идемпотентности: если клиент переотправляет после таймаута, сервер не создаёт дубль.
Conversation: id, participant_ids[], last_message_id, last_message_at. Индекс: (participant_ids, last_message_at DESC) — чтобы список диалогов у пользователя выбирался быстро.
Пагинация истории
Cursor-based по (created_at, id): загружаем последние N сообщений, при скролле вверх передаём before_cursor. Это работает стабильно при быстрой вставке новых сообщений — OFFSET «плывёт», когда между страницами появляются новые записи.
На iOS список сообщений — UICollectionView с инвертированным layout (новые снизу): transform = CGAffineTransform(scaleX: 1, y: -1) на collectionView и каждой ячейке. При добавлении нового сообщения insertItems с scrollToItem — без reloadData который вызывает мерцание. На Android — LazyColumn(reverseLayout = true) в Jetpack Compose.
Статусы доставки и прочтения
Delivered: сервер подтверждает получение сообщения (ack в WS-протоколе) и обновляет статус. Read: клиент-получатель отправляет read receipt когда conversation открыта и сообщение видимо на экране (через Intersection Observer на вебе или через UICollectionView.indexPathsForVisibleItems на iOS).
Статусы в UI: одна галочка (sent), две серых (delivered), две синих (read) — классика. Меняем через локальный update в DiffableDataSource без сетевого запроса.
Шифрование
Для базового end-to-end: Signal Protocol через libsignal-client (Rust-библиотека с биндингами для iOS/Android). Ключи хранятся в Keychain (iOS) / Android Keystore. Сервер видит только зашифрованный blob — даже при компрометации БД переписка не читается.
Если E2E не обязательно — шифрование на транспорте (TLS 1.3) + шифрование в покое на стороне сервера достаточно для большинства случаев.
Push-уведомления когда чат закрыт
FCM (Android) и APNs (iOS). На iOS нужен UNUserNotificationCenter + UNNotificationServiceExtension если нужно показать превью зашифрованного сообщения: Extension расшифровывает payload перед показом без передачи ключей серверу.
Deep link при тапе на пуш — открывает конкретный conversation: myapp://chat/conversation/{id}. Реализуется через UIApplicationDelegate.application(_:open:options:) или onOpenURL в SwiftUI.
Этапы и сроки
Базовый чат (WS-соединение, история с пагинацией, статусы, пуши) — 5 рабочих дней на одну платформу. Добавление медиа-вложений, E2E-шифрования, индикатора набора текста (typing indicator через WS-event) — ещё 3-5 дней. Flutter — немного быстрее за счёт единой кодовой базы. Стоимость разработки чата под ключ зависит от сложности и начинается от 100 000 ₽. Свяжитесь с нами для точной оценки вашего проекта.







