A native macOS email client that doesn't think it's a browser.
Suave Mail is an open-source, privacy-first Gmail client for macOS built entirely with SwiftUI and SwiftData. No Electron. No Chromium embedded inside a trench coat pretending to be a desktop app. Just pure, native Swift talking directly to the Gmail API — the way it should be.
Because:
- Electron email clients use more RAM than your entire development environment
- The built-in Mail app has a complicated relationship with Gmail labels
- Web Gmail is fine until you want keyboard shortcuts that actually make sense
- You believe that a 2024 Apple Silicon Mac should open an email in under 50ms
If you nodded at any of the above, welcome home.
Suave Mail follows a "Lean & Calm" design principle:
- OLED-black backgrounds — easier on the eyes, easier on the soul
- Floating sidebar with translucent
.thinMaterial— no harsh borders, just layers of depth - Generous whitespace — because your inbox is already stressful enough
- Apple serif fonts (New York) in reading pane with 1.8× line height — emails should be readable, not a wall of Arial 13pt
- Inbox Zero celebration — a breathing animation greets you when you've actually made it. Take a deep breath. You earned it.
Suave Mail is built around the assumption that your emails are none of anyone else's business — including the app itself.
- Zero data leaves your device. All emails are fetched, sanitized and stored locally via SwiftData. No telemetry, no analytics, no "improving your experience."
- HTML Sanitizer strips tracking pixels, analytics scripts and malicious code before rendering — at the Swift layer, before it even reaches WebKit.
- WKContentRuleList blocks external trackers directly at the WebKit engine level.
- Privacy Dashboard in the sidebar shows you exactly how many spy pixels tried to phone home today. (Spoiler: it's more than you think.)
- Keychain-only token storage. OAuth tokens live in the macOS Keychain. If Google revokes your token, the app wipes the session immediately instead of endlessly retrying in a broken refresh loop.
SuaveMail (macOS App)
├── SuaveMailCore — Business logic, no UI dependencies
│ ├── Auth/ — OAuth2 via AppAuth, Keychain storage
│ ├── Gmail/ — Gmail REST API client + sync engine
│ │ └── DTOs/ — Codable response models
│ ├── Models/ — SwiftData models (MailThread, Message, Account…)
│ ├── Persistence/ — MailRepository, PersistenceController
│ └── MailRendering/ — HTMLSanitizer, CSS injection
│
└── SuaveMailUI — SwiftUI views, no direct API calls
├── Auth/ — LoginView
├── Shell/ — AppShell, AppShellModel (paginated)
├── Sidebar/ — Labels, Privacy Dashboard
├── ThreadList/ — Paginated thread list
└── Detail/ — MailWebView (WKWebView + sanitizer)
| Layer | Technology |
|---|---|
| Language | Swift 6.x (strict concurrency) |
| UI | SwiftUI (macOS 14+) |
| Persistence | SwiftData + SQLite |
| Authentication | AppAuth + macOS Keychain |
| Email API | Gmail REST API (not IMAP) |
| Rendering | WKWebView + custom HTMLSanitizer |
| Privacy | WKContentRuleList (WebKit-level blocking) |
- macOS 14.0+
- Xcode 16+
- A Google Cloud project with the Gmail API enabled
- An OAuth 2.0 Client ID that matches
com.gs.SuaveMail - A tester account added to the Google OAuth consent screen when the app is in testing mode
# 1. Clone
git clone https://github.com/Giorgio089/suave-mail.git
cd suave-mail
# 2. Open in Xcode
open SuaveMail.xcodeproj- Go to Google Cloud Console
- Create a project → Enable the Gmail API
- Create an OAuth 2.0 Client ID that exports a Google client
plist - Confirm the exported values match the repo config:
CLIENT_IDREVERSED_CLIENT_IDBUNDLE_ID = com.gs.SuaveMail - If you rotate projects again later, update these keys in Info.plist:
GMAIL_OAUTH_CLIENT_IDGMAIL_OAUTH_REDIRECT_URICFBundleURLSchemes - Ensure the OAuth consent screen is in testing or production as intended and add the testers who should be able to log in
- Ensure the configured Gmail scopes cover the current app feature set:
https://www.googleapis.com/auth/gmail.readonlyhttps://www.googleapis.com/auth/gmail.sendhttps://www.googleapis.com/auth/gmail.modify - Build & Run (
Cmd+R)
Note: The repo now versions a working tester-ready OAuth client configuration. The client ID is not a secret. Secrets still stay out of the repo.
| Shortcut | Action |
|---|---|
Cmd+N |
New message |
Cmd+R |
Refresh |
Cmd+K |
Search |
# Run all tests
./Scripts/test.sh
# Check formatting
./Scripts/format.sh --check
# Lint
./Scripts/lint.shCI runs automatically on every push and pull request via GitHub Actions (.github/workflows/ci.yml). It builds, tests, lints and formats on macOS 15.
suave-mail/
├── Sources/
│ ├── SuaveMailApp/ — App entry point, scene setup
│ ├── SuaveMailCore/ — Core logic (no UIKit/SwiftUI)
│ └── SuaveMailUI/ — All SwiftUI views
├── Tests/
│ ├── SuaveMailCoreTests/
│ └── SuaveMailUITests/
├── Assets.xcassets/
├── Scripts/ — build, test, lint, format helpers
├── Package.swift — Swift Package Manager manifest
├── Info.plist — versioned Gmail OAuth/tester config
├── DESIGN.md — Original design document & motivation
└── ARCHITECTURE.md — Current architecture & roadmap
- OAuth2 login + Keychain storage
- Gmail API sync engine (threads, labels, history delta)
- Paginated thread list (no full-load crashes on large inboxes)
- HTML email rendering with sanitization & tracking protection
- Privacy Dashboard
- Zen UI: floating sidebar, OLED dark mode, New York typography
- Keyboard shortcuts
- Background sync via
NSBackgroundActivityScheduler - Full-text search via SwiftData predicates (
Cmd+K) - Composer / Send
- CloudRelayService — real-time push without breaking the zero-data promise
PRs welcome! The codebase is split cleanly between SuaveMailCore (zero UI dependencies) and SuaveMailUI — making it easy to add features or tests without touching unrelated code.
If you find a tracking pixel that slips through the sanitizer, please open an issue. We take that personally.
MIT — build on it, learn from it, ship your own mail client. The world needs more native apps.
Built for people who believe their inbox deserves better than a 400MB Electron bundle.