Understanding the Problem
------------------------
When it comes to designing a mobile email client that mirrors Gmail's functionality, there are several key considerations to keep in mind. For instance, an email client should be able to synchronize multiple server accounts, store messages locally, draft emails offline, queue sends when connectivity is lost, and surface push notifications for new mail. This requires careful attention to detail, particularly when it comes to handling large amounts of data.
Clarifying Questions
1. How many daily-active iOS devices?
To size disk and fan-out push notifications effectively, we'll assume approximately 300 million DAU (daily active users). This will help us determine the necessary resources required for our mobile email client.
2. Peak simultaneous foreground users?
Using 2% of DAU, we can estimate around 6 million phones simultaneously using the app. This figure will help us set an upper bound for HTTP/2 connection pools.
3. Average new messages per user per day?
Assuming an average of 120 new emails per user per day, this translates to approximately 18 items or silent push notifications. This information is crucial for our email client's performance and scalability.
4. Typical history-list diff size?
With a typical delta size of around 0.9 KB JSON, we can fit within the ≤2 MB/day data SLO (service-level objective). This ensures our mobile app development meets performance requirements.
5. Message size distribution?
According to Gmail SRE talks, the P50 message size is approximately 32 KB, while the P95 message size is around 400 KB. This information will help us optimize attachment handling and reduce storage requirements.
6. Offline cache window?
To ensure efficient offline caching, we'll set a window of 90 days or ≤5 GB, whichever comes first. This ensures our mobile app development can handle large amounts of data while minimizing storage requirements.
Functional Requirements
-------------------------
When designing an email client similar to Gmail, several functional requirements must be met:
- Send and receive mail
- Threaded conversations
- Offline viewing
- Draft autosave
- Queue when offline
- Push notifications for new mail
- Accessibility and localization
- Multiple accounts
Staff Tip
To demonstrate a strong understanding of these functional requirements, consider diving deeper into a specific requirement, such as send/receive mail. For instance, you could guarantee exactly-once delivery by persisting drafts before network writes, using ULID Message-ID for dedupe, and implementing idempotent server writes.
Non-Functional Requirements
---------------------------
In addition to meeting functional requirements, our email client must also meet certain non-functional requirements:
- Scroll: 60 FPS with ≤4 ms CPU/frame
- Search: local match within ≤200 ms
- Cold-start: first paint within ≤1.5 s (Google KPI)
- Battery: background usage ≤1%/h
- Sync: delta-only with ≤2 MB/day
- Storage: support up to 250,000 messages and 10 GB attachments, pruning beyond 5 GB
Staff Tip
To demonstrate a strong understanding of these non-functional requirements, consider validating each requirement through metrics, traces, or CI performance tests. This shows ownership beyond code.
Out-of-Scope
-------------
Certain mechanisms are out of scope for this question:
- TLS/S/MIME key management details
- Full attachment CDN and resumable-upload service
- OAuth flow and refresh-token lifecycle
Rough Capacity Planning
------------------------
For launch-day mobile-only capacity planning, consider the following rough estimates:
- Active devices: approximately 300 million DAU-logged-in phones/tablets that open the app at least once per day
- Incoming mail: around 36 billion messages per day (120 messages × 300 million users)
- Outgoing mail: approximately 4 billion messages per day (~12 sends/user; heavy skew toward power users)
- Threads touched: around 20 billion per day (conversation containers fetched/updated)
- Attachments downloaded: approximately 14 billion per day (~40% of messages carry ≥1 attachment; detached >4 KB parts only)
- Push notifications: around 5.4 billion per day (18 silent pushes × 300 million devices)
- Local cache window: 90 days or ≤5 GB, whichever comes first
- Sync API read/write ratio: approximately 150:1 (foreground reads, background delta pulls dwarf user-initiated writes)
API (REST facade ⇄ gRPC)
-------------------------
For our mobile email client's API design, consider using a REST facade to interface with the gRPC service. This allows for efficient communication and reduces latency.
Request Body:
`
{ "raw": "RGF0ZTogV2VkLCA2IEp1biAyMDI1IDA5OjAwOjAwICswMDAwDQpTdWJqZWN0OiBIaSEgVGhpcyBpcyBhIHRlc3QgbWFpbA0KUHJlZmVyZW5jZTogPGYxQDE4ZDkxLjFlZTg4YzQuMTJhZUBtYWlsLmdtYWlsLmNvbT4NCkZyb206IGFsaWNlQGV4YW1wbGUuY29tDQpUbzogYm9iQGV4YW1wbGUuY29tDQoNCkhpIEJvYiENCg0KVGhpcyBpcyBqdXN0IGEgdGVzdCBtZXNzYWdlLi4uIg }
`
Request Body:
`
{ "message": { "raw": "RGF0ZTogLi4u", "labelIds": [ "DRAFT" ] } }
`
Request Body:
`
{ "name": "photo.jpg", "mimeType": "image/jpeg", "size": 1048576 // bytes }
`
Request Body:
`
{ "ids": [ "18d904f3ac5c6ab7e", "18d904f3ac5c6ab7f" ], "addLabelIds": [ "STARRED" ], "removeLabelIds": [ "UNREAD" ] }
`
Why choose gRPC for mobile?
------------------------------
gRPC offers several benefits for our mobile email client:
- Schema-Driven: Protobuf enforces compile-time safety.
- HTTP/2 Multiplexing: Many concurrent RPCs on one TLS socket, reducing latency and improving performance.
- Binary Payloads: Approximately 30% smaller than JSON, making it more efficient in terms of data transfer.
- Streaming: Resumable attachment uploads/downloads without extra control calls.
High-Level Design
-------------------
UI Flow & State
Our UI layer will be built using SwiftUI or Jetpack Compose with an Observable Snapshot of the UI and state. Most processes will occur on the main thread.
Presentation (ViewModel)
These are KVO objects that might process things off the main thread through the Domain layer (via dependency injection) but mutate the UI state on the main thread.
Domain Actors
These are serial executors that operate on their own queue, making them thread-safe and capable of orchestrating IO, business logic, idempotency, etc.
Repositories
Async CRUD helpers with CoreData background context (shared connection pool + scratch-pad).
Persistence
Hot metadata read path, raw bodies for attachments, blobs mmap'd at render time.
Network Transport
Networking, token refresh, and resumable uploads using gRPCClient (HTTP/2), PushNotificationHandler, UploadManager, and more.
System Helpers
Background CPU, secure credential store through BGAppRefreshTask, BGProcessingTask, Keychain, and Secure Enclave.