Разработка мобильного приложения для мессенджера
Мессенджер — это не «чат через WebSocket». Это система с end-to-end шифрованием, надёжной доставкой сообщений при нестабильной сети, синхронизацией между устройствами, эффективным хранением истории на клиенте и приемлемым потреблением батареи. Каждый из этих пунктов — отдельная инженерная задача.
Протокол и доставка сообщений
WebSocket — базовый транспорт для real-time обмена. Но WebSocket на мобильных не держится вечно: операционная система убивает фоновые соединения. iOS не позволяет держать WebSocket в background без Background Modes: Remote notifications. Android с Doze Mode закрывает соединение при неактивности.
Правильная архитектура: WebSocket в foreground + FCM/APNs push для wake-up в background. При получении push клиент поднимает соединение и скачивает сообщения. На iOS background payload через content-available: 1 даёт 30 секунд на скачивание новых сообщений.
Гарантированная доставка. Каждое сообщение имеет client-generated UUID и порядковый номер (sequence number) per-conversation. Клиент сохраняет сообщение в локальную БД со статусом sending, отправляет на сервер, после ack сервера — меняет статус на sent. При потере соединения — очередь неотправленных сообщений, retry при восстановлении. Статусы: sending → sent (сервер получил) → delivered (клиент получателя принял) → read.
// iOS — управление статусом доставки сообщения
enum MessageStatus: String, Codable {
case sending, sent, delivered, read, failed
}
class MessageStore {
func sendMessage(_ text: String, to conversationId: String) {
let msg = Message(
id: UUID().uuidString,
conversationId: conversationId,
body: text,
status: .sending,
timestamp: Date()
)
coreDataContext.insert(msg) // сохраняем сразу
webSocketClient.send(msg) { [weak self] result in
switch result {
case .success: self?.updateStatus(msg.id, .sent)
case .failure: self?.updateStatus(msg.id, .failed)
}
}
}
}
End-to-End шифрование
E2EE — не опция для серьёзного мессенджера. Signal Protocol — индустриальный стандарт: Double Ratchet algorithm + X3DH (Extended Triple Diffie-Hellman) key agreement. Реализация: libsignal (официальная библиотека Signal Foundation, порты для iOS и Android).
Схема: при регистрации генерируются ключи (identity key, signed prekey, one-time prekeys). Публичные части загружаются на сервер. При начале чата — клиент скачивает prekey получателя, выполняет X3DH, устанавливает шифрованную сессию. Сервер никогда не видит plaintext сообщений.
Бэкап зашифрованной переписки. Если переписка E2EE, бэкап в iCloud/Google Drive должен быть зашифрован независимым ключом, который хранится только у пользователя (derived from PIN/passphrase). WhatsApp делал это через HSM-backed key backup, что было предметом споров — более правильно: полностью client-side ключ.
Хранение истории на клиенте
SQLite через Room (Android) или CoreData / GRDB (iOS). Схема: conversations, messages, attachments, reactions. Индексы по conversation_id + timestamp для быстрой загрузки ленты. Full-text search по messages.body через FTS5 (SQLite full-text search extension).
Пагинация истории — reverse cursor: загружаем N последних сообщений, при скролле вверх запрашиваем следующие N. Хранить полную историю локально нецелесообразно: ограничение на N последних сообщений per-conversation, остальное — lazy load с сервера.
Медиа в мессенджере
Фото, видео, документы — отдельный upload pipeline. Presigned URL → upload напрямую в object storage → отправить ссылку в сообщении. Thumbnail генерируется клиентом и прикрепляется к сообщению как base64 blurred preview (blurhash algorithm) — это позволяет показать placeholder до загрузки оригинала.
Голосовые сообщения: запись через AVAudioRecorder (iOS, opus через AVAudioSession) или MediaRecorder (Android). Кодек — Opus, 24 кбит/с достаточно для речи. Waveform preview — амплитуды семплов, нормализованные до отображаемого размера.
Сжатие изображений перед отправкой. UIGraphicsImageRenderer с максимальным размером 1280px и JPEG quality 0.8. Без этого каждое фото с iPhone 15 Pro — 12+ МБ трафика.
Группы и каналы
Group chat до ~1000 участников — стандартный fan-out. Broadcast-каналы на десятки тысяч подписчиков — асинхронная доставка через очередь (Kafka). Для E2EE в группах: Sender Keys (как в Signal/WhatsApp) — один шифрованный поток для всех участников группы, а не N индивидуальных сессий.
Упоминания (@username) в группе: push только упомянутому пользователю или с настраиваемыми уведомлениями per-group.
Батарея и push optimization
Keepalive для WebSocket — каждые 20–30 секунд ping/pong. Слишком часто — расходует батарею. Редко — соединение рвётся без обнаружения (TCP keepalive на уровне ОС не всегда помогает через NAT). Оптимальный интервал: 25 секунд (iOS закрывает сокеты без активности через 30 с).
APNs Priority 5 (low priority) для "фоновых" уведомлений — не будит экран, не вибрирует, OS сама решает когда доставить. Priority 10 (high) — только для явных входящих сообщений.
Сроки
MVP с чатом, медиа и push-уведомлениями без E2EE: 6–8 недель. Полноценный мессенджер с E2EE, голосовыми, группами и бэкапом: 3–5 месяцев. Стоимость рассчитывается индивидуально после анализа требований.







