Розробка мобільного додатку для мессенджера
Мессенджер — це не «чат через WebSocket». Це система з end-to-end шифруванням, надійною доставкою сообщень при нестабільній мережі, синхронізацією між пристроями, ефективним зберіганням історії на клієнті та прийнятним споживанням батареї. Кожна з цих пунктів — окремої інженерна задача.
Протокол та доставка сообщень
WebSocket — базовий транспорт для real-time обміну. Але WebSocket на мобільних не тримається вічно: операційна система убиває фонові з'єднання. iOS не дозволяє тримати WebSocket у фоні без Background Modes: Remote notifications. Android з Doze Mode закриває з'єднання при неактивності.
Правильна архітектура: WebSocket у foreground + FCM/APNs push для wake-up у фоні. При отриманні push клієнт піднімає з'єднання та завантажує сообщення. На iOS background payload через content-available: 1 дає 30 секунд на завантаження.
Гарантована доставка. Кожне сообщення має client-generated UUID та порядковий номер 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.
Пагінація історії — 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 якістю 0.8. Без цього кожне фото з iPhone 15 Pro — 12+ МБ трафіку.
Групи та канали
Group chat до ~1000 учасників — стандартний fan-out. Broadcast на десятки тисяч підписників — асинхронна доставка через чергу (Kafka). Для E2EE у групах: Sender Keys (як у Signal/WhatsApp) — один шифрований потік для всіх учасників групи, а не N індивідуальних сесій.
Упоминання (@username) у групі: push тільки упомянутому користувачу або з налаштовуваними уведомленнями per-group.
Батарея та push оптимізація
Keepalive для WebSocket — кожні 20–30 секунд ping/pong. Занадто часто — розходує батарею. Рідко — з'єднання рветься без виявлення. Оптимально: 25 секунд (iOS закриває сокети без активності через 30 с).
APNs Priority 5 (low priority) для «фонових» уведомлень — не будить екран, не вібрує, OS сама вирішує коли доставити. Priority 10 (high) — тільки для явних вхідних сообщень.
Графіки
MVP з чатом, медіа та push без E2EE: 6–8 тижнів. Повноцінний мессенджер з E2EE, голосовими, групами та бекапом: 3–5 місяців. Вартість розраховується індивідуально після аналізу вимог.







