Skip to main content

Design a Messaging App

Concepts Utilized: WebSocket Connection, Local Database (SQLite/Realm), Offline Queue, Push Notifications, End-to-End Encryption, Message Syncing, Media Compression, Read Receipts, Background Service

This document covers the mobile architecture for a messaging application with real-time delivery, offline support, and media sharing.

Requirements

Functional Requirements

FeatureDescription
Text messagingSend and receive text messages
ConversationsOne-on-one and group conversations
Media sharingImages, videos, and file attachments
Message statusSent, delivered, and read indicators
Push notificationsAlerts for new messages
Offline supportQueue messages when offline, sync when connected
SearchFull-text search across message history

Non-Functional Requirements

RequirementTarget
Message delivery latencyUnder 500ms
Offline capabilityFull read/write functionality
Battery usageMinimal background consumption
Storage efficiencyOptimized for limited device storage

Architecture Overview

Loading diagram...

Core Components

1. Local Database Schema

Conversations table: Stores conversation metadata including ID, type (direct or group), name, avatar URL, last message reference for sorting, unread count, muted status, and timestamps.

Messages table: Stores message data including ID, conversation reference, sender, content, content type (text, image, video, file), delivery status (pending, sent, delivered, read, failed), media URLs (remote and local), reply reference, and various timestamps for status tracking.

Outbox table: Holds pending messages for offline queue processing with message reference, serialized payload, retry count, and creation timestamp.

Indexes: Create indexes on messages by conversation and timestamp for chat history queries, by status for pending message processing, and on conversations by last message time for sorting the conversation list.

2. Message Flow

Sending a Message

Loading diagram...

Receiving a Message

Loading diagram...

3. Message Status State Machine

Loading diagram...

4. Offline Support

Message Queue Implementation

Enqueue operation:

  1. Save message to local database with pending status
  2. Add to outbox table for queue processing
  3. Attempt immediate send if network is available

Send pending messages:

  1. Retrieve all outbox entries
  2. For each entry, attempt network send
  3. On success: remove from outbox, update message status to sent
  4. On failure: increment retry count; mark as failed if max retries exceeded

Network restoration: When connectivity returns, trigger processing of all pending messages in the queue.

Sync Strategies

ScenarioStrategy
App opens after being closedFetch messages since last sync timestamp
Connection restored after offlineSend queued messages, fetch new messages
Background fetch (iOS)Sync recent messages, update badges
Push notification tapFetch specific conversation

Real-Time Communication

WebSocket Connection

Connection management:

  • On connect: Reset reconnect counter, start heartbeat timer, subscribe to message channels
  • On message: Parse and route to message handler
  • On disconnect: Stop heartbeat, schedule reconnection

Reconnection strategy: Use exponential backoff with a maximum delay (such as 30 seconds). Track reconnect attempts to calculate delay. After each failed attempt, wait longer before retrying.

Heartbeat: Send periodic ping messages (every 30 seconds) to maintain the connection and detect disconnections promptly.

Push Notifications

Push notifications deliver messages when the WebSocket connection is inactive.

Implementation approach:

  1. Handle remote notification in the app delegate method
  2. Extract message data from the notification payload
  3. Parse and save the message to the local database
  4. Query total unread count and update the app badge number
  5. Call the completion handler with the appropriate result

Media Handling

Image Upload Flow

Loading diagram...

Image Sizing

SizeDimensionsUse Case
Thumbnail100x100Conversation list preview
Preview300x300Chat bubble preview
Full1200xAutoFull screen view
OriginalAs-isDownload option

Media Caching

Two-tier caching strategy:

  1. Create cache key combining URL and size
  2. Check memory cache first (NSCache); return if found
  3. Check disk cache; if found, promote to memory cache and return
  4. If not cached, return nil (caller should download)

Cache writes: Store in both memory and disk caches. Memory cache provides fast access for current session; disk cache persists across app restarts.

Performance Optimizations

1. Message List Virtualization

Render only visible messages using lazy loading.

UIKit approach: Use UITableViewDiffableDataSource to manage message display. Create snapshots with updated messages and apply with automatic animation handling.

SwiftUI approach: Use LazyVStack inside ScrollView. Only visible message bubbles are rendered; others are created on demand as the user scrolls.

2. Database Query Optimization

Paginated loading: Query messages with a limit and cursor (timestamp). Select from messages where conversation matches and timestamp is before the cursor, ordered by timestamp descending, with a fixed limit. This enables loading older messages in chunks as the user scrolls up.

3. Batch Operations

Inefficient approach: Looping through messages and updating status individually triggers multiple database writes and notifications.

Efficient approach: Execute a single SQL UPDATE statement with a WHERE clause matching all relevant messages. This performs one database operation regardless of message count.

4. Background Processing

Implementation approach: Dispatch CPU-intensive operations (like image compression) to a background queue. When complete, dispatch back to the main queue for UI updates or network operations.

Battery Optimization

TechniqueImplementation
Batch network requestsCombine multiple operations into single requests
Smart sync intervalsIncrease frequency when app is active
Efficient WebSocketSingle connection shared across conversations
Background fetch limitsRespect iOS/Android background execution quotas
Image compressionReduce upload sizes to minimize transmission time

Security

End-to-End Encryption (E2EE)

Loading diagram...

Key Storage

PlatformSecure Storage API
iOSKeychain Services
AndroidAndroid Keystore

Summary

DecisionOptionsRecommendation
Data persistenceCore Data, SQLite, RealmSQLite with wrapper for flexibility
Real-time transportPolling, WebSocket, SSEWebSocket for bidirectional communication
Offline strategyQueue & syncLocal-first with outbox pattern
Image handlingUpload then send, Inline base64Upload first, send URL reference
List renderingUITableView, SwiftUI ListDiffable data source or LazyVStack