Developing a Messenger Mobile App
A messenger isn't just "chat via WebSocket." It's a system with end-to-end encryption, reliable message delivery over unstable networks, cross-device sync, efficient local history storage, and acceptable battery consumption. Each of these is a separate engineering task.
Protocol and Message Delivery
WebSocket—basic transport for real-time. But WebSocket on mobile doesn't persist: OS kills background connections. iOS doesn't allow WebSocket in background without Background Modes: Remote notifications. Android Doze Mode closes connections during inactivity.
Right architecture: WebSocket in foreground + FCM/APNs push for background wake-up. On push, client raises connection and fetches messages. On iOS background payload via content-available: 1 gives 30 seconds for download.
Guaranteed delivery. Each message has client-generated UUID and sequence number per-conversation. Client saves locally with sending status, sends to server, after server ack changes to sent. On connection loss—queue unsent messages, retry on recovery. Statuses: sending → sent (server got it) → delivered (recipient client accepted) → read.
// iOS — message delivery status management
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) // save immediately
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 Encryption
E2EE—non-optional for serious messenger. Signal Protocol—industry standard: Double Ratchet algorithm + X3DH (Extended Triple Diffie-Hellman) key agreement. Implementation: libsignal (official Signal Foundation library, iOS and Android ports).
Scheme: on registration, keys generated (identity key, signed prekey, one-time prekeys). Public parts uploaded to server. On chat start—client fetches recipient prekey, performs X3DH, establishes encrypted session. Server never sees plaintext.
Encrypted message backup. If E2EE, backup to iCloud/Google Drive must be independently encrypted with key held only by user (derived from PIN/passphrase). WhatsApp did this via HSM-backed key backup, debated—more correct: fully client-side key.
Client-Side History Storage
SQLite via Room (Android) or CoreData / GRDB (iOS). Schema: conversations, messages, attachments, reactions. Indexes on conversation_id + timestamp for fast feed loading. Full-text search on messages.body via FTS5.
History pagination—reverse cursor: load N latest messages, on scroll up request next N. Storing full history locally impractical: limit to N recent per-conversation, rest lazy-load from server.
Media in Messenger
Photo, video, documents—separate upload pipeline. Presigned URL → upload directly to object storage → send link in message. Thumbnail generated by client, attached as base64 blurred preview (blurhash algorithm)—shows placeholder before original loads.
Voice messages: record via AVAudioRecorder (iOS, opus via AVAudioSession) or MediaRecorder (Android). Codec—Opus, 24 kbps sufficient for speech. Waveform preview—normalized sample amplitudes.
Image compression before send. UIGraphicsImageRenderer with max 1280px and JPEG quality 0.8. Without this each iPhone 15 Pro photo is 12+ MB traffic.
Groups and Channels
Group chat up to ~1000 members—standard fan-out. Broadcast to tens of thousands—async delivery via queue (Kafka). For E2EE in groups: Sender Keys (Signal/WhatsApp)—one encrypted stream for all members, not N individual sessions.
Mentions (@username) in group: push only to mentioned or with configurable per-group notifications.
Battery and Push Optimization
WebSocket keepalive—every 20–30 seconds ping/pong. Too frequent—drains battery. Rare—connection breaks undetected. Optimal: 25 seconds (iOS closes sockets after 30s inactive).
APNs Priority 5 (low) for "background" notifications—doesn't wake screen, no vibration, OS decides delivery time. Priority 10 (high)—only for explicit incoming messages.
Timelines
MVP with chat, media, push without E2EE: 6–8 weeks. Full messenger with E2EE, voice, groups, backup: 3–5 months. Cost calculated individually after analyzing requirements.







