Mes
Designing AI for the most intimate digital surface.
Concept to App Store. Solo. Four months.
Designer & engineer · SwiftUI + Gemini · Live on the App Store
A reply you send is a reply from you. Mes makes generative AI feel like yours by letting you steer its voice before it speaks, not audit it after.
Adding AI to iMessage is not adding a feature. It's editing the most personal app on someone's phone.
This case study is about what that design problem actually feels like.
Designing for AI is designing for uncertainty.
The hardest part wasn't the model. It was the mental model.
Generative AI in iMessage is high-stakes by default.
Generative AI in a productivity tool is forgiving. If a summary is slightly off, users can fix it. In iMessage the stakes are different. A reply you send is a reply from you. Wrong tone breaks the relationship. Too generic and you sound robotic. Too clever and you sound like an AI wrote it.

Three options, not one.
A single "best" reply forces a binary accept-or-rewrite decision. Three options give users a comparison space, and comparison shifts the user from evaluator to editor. They pick the closest match and tweak. That mental shift is everything.

Tone as a first-class control.
Most AI reply UIs hide tone behind a generate button. Mes surfaces it as the primary interaction: three chips, picked before generating. The user steers the AI's voice rather than auditing its output. Trust comes from agency.

Bubbles, not list items.
Reply options render as iMessage bubbles, not as menu rows. The user sees the message in the form it will take. The UI metaphor matches the destination, leaving no translation step between preview and reality. And nothing sends on its own: the reply you pick drops into the compose field to edit first, so the AI drafts but never speaks for you.





Generate a reply.
Pre-canned tones, but the choreography is exactly how the real feature behaves.
Trust in generative UI comes from agency, not accuracy.
Six tools. One language.
The AI Reply feature is the headliner. Mes is a platform. Five additional tools cover the rest of what group chats need, each built with the same component vocabulary, each ship-quality on its own.

Reply in your voice, not the AI's.
Three tones, three options each, generated by Gemini 2.5 Flash. The user picks the tone before generating, steering the AI's voice rather than auditing its output.
Real-time polling. No backend.
Interactive cards that update inside iMessage as participants vote. Multi-choice and star-rating. State lives inside the MSMessage URL: no server, no sync, no lag.
Settle the bill in three taps.
Even or custom splits for up to 10 people. Tip presets and custom percentages. One-tap Venmo or Cash App handoff per person, routed through the main app to satisfy iMessage sandboxing.
Let the wheel decide.
A weighted decision tool for groups. Spring-physics spin animation tuned for natural deceleration. Result card auto-inserts to the thread.
Count to what matters.
Shared event countdowns rendered in the recipient's local time. Cards update each time the thread is opened. No backend, just smart re-rendering.
Reactions, curated.
Six packs covering the full emotional range of group chat. Sticker store syncs across the main app and extension via App Group. Cat Reacts ships free.
One system, two targets.
Mes ships as two Apple targets that share a single design language: a main app for paywall, onboarding, and the sticker store, and an iMessage extension for the tools themselves. Every component had to work in both. The component library lives in production Swift code, not just in a design file.
Mascot.
A plush bunny named Mes, designed to express the brand's emotional range through a single accessory. No crown in onboarding: welcoming, approachable. Gold crown in the paywall: aspirational. One character, two registers.
Color tokens.
Deep midnight base, a saturated iMessage-blue accent, and a warm cream for text, never pure white. Two hairline tints carry every division.
Typography.
Bricolage Grotesque carries every display moment; Geist handles body and UI; JetBrains Mono labels the technical and editorial eyebrows.
Components.
Bubble UI as the dominant metaphor. Liquid-glass pills, a custom bounce button, spring-physics progress, each a reusable view shared by both targets.
Design → Xcode.
Every component existed in design and SwiftUI simultaneously. There was no design handoff, only the same person committing both files to git in the same hour.
Three constraints. Three design decisions.
iMessage extensions are the most-constrained surface Apple ships. Every limit became a design problem. Each design solution shaped the product's character.
The 60MB ceiling.
iMessage extensions are hard-capped at 60MB of memory. The AI model can't run inside the extension. Calling the Gemini API directly is a memory crash waiting to happen.
The main app becomes the AI brain. The extension stays a thin client. API calls fire from the main app and write results to an App Group container. The bridge: a URL scheme handler (mes://) that the user never sees but the architecture depends on.
AI responses had to feel instant in the extension even though they were generated outside it. The solution was eager pre-generation: the extension speculatively requests replies before the user has even picked a tone.
// AppGroupBridge.swift: extension → main app communication.
// iMessage extensions have a 60MB ceiling, so AI calls run
// in the main app and results sync via App Group.
enum AppGroupBridge {
static let appGroupID = "group.com.gya.mes"
static let defaults = UserDefaults(suiteName: appGroupID)!
static func requestReplies(for incoming: String, tone: Tone) {
defaults.set(incoming, forKey: "pendingMessage")
defaults.set(tone.rawValue, forKey: "pendingTone")
NotificationCenter.default.post(name: .mesAIRequest, object: nil)
}
static func cachedReplies() -> [String] {
defaults.stringArray(forKey: "cachedReplies") ?? []
}
}
No UIApplication.shared.open.
Inside an iMessage extension, you can't open URLs the normal iOS way. Apple's sandboxing rules forbid it. This breaks Venmo, Cash App, every deep-linking pattern designers reach for first.
Every payment handoff goes through a NotificationCenter event the main app observes. The extension posts the intent. The main app acts on it. From the user's perspective, tapping Venmo opens Venmo, while the actual call happens in a different process.
The bill split UI couldn't promise instant handoff, so it doesn't try to. Buttons animate to an "Opening Venmo…" state for ~200ms before the app switches. The micro-interaction covers the architectural seam.
iMessage is stateless.
There's no backend in an iMessage app. Every piece of state (poll votes, wheel results, countdown updates) has to live inside the MSMessage URL component itself. State exists in the message. The message is the state.
Polls re-render and re-insert into the thread on every vote, replacing the previous version. The card looks live, but it's actually being recomposed each time. The user perceives real-time updates. The system performs stateless replacements.
Visual continuity had to do the work that backend persistence usually does. Spring-physics animations make recomposed cards feel like the same card updating rather than a new card replacing the old one.

Four months. One person. One ship.
Competitive research across 400+ existing iMessage apps. Identified the gap: no single app handled polls + AI reply + bill split at design-award quality. Wrote PRD v2 with Apple Design Award as the explicit north star.
Competitive research across 400+ existing iMessage apps. Identified the gap: no single app handled polls + AI reply + bill split at design-award quality. Wrote PRD v2 with Apple Design Award as the explicit north star.
Designed the dual-target architecture: main app (paywall, onboarding, sticker store) + extension (all six tools). App Group as the shared data layer. Gemini API routed through the main app only. The constraint shaped the architecture, the architecture shaped the UX.
Designed the dual-target architecture: main app (paywall, onboarding, sticker store) + extension (all six tools). App Group as the shared data layer. Gemini API routed through the main app only. The constraint shaped the architecture, the architecture shaped the UX.
Ten build sessions across six weeks. Each feature built as an isolated SwiftUI module. The bubble UI system, liquid glass cards, and dark blue extension aesthetic developed in parallel, not after, so the visual language emerged from the components, not the other way around.
Ten build sessions across six weeks. Each feature built as an isolated SwiftUI module. The bubble UI system, liquid glass cards, and dark blue extension aesthetic developed in parallel, not after, so the visual language emerged from the components, not the other way around.
Plush bunny mascot finalized. Fluffy fabric texture paywall. SF Rounded typography. Animated onboarding with looping video backgrounds. Every component documented in Swift and the design files simultaneously.
Plush bunny mascot finalized. Fluffy fabric texture paywall. SF Rounded typography. Animated onboarding with looping video backgrounds. Every component documented in Swift and the design files simultaneously.
Rejected. Guideline 2.1 (App Review wanted a screen recording showing IAP flow end-to-end). Guideline 3.1.2c (free trial toggle on the paywall + EULA link requirement). Primary language accidentally set to Danish, which cannot be reverted in App Store Connect.
Rejected. Guideline 2.1 (App Review wanted a screen recording showing IAP flow end-to-end). Guideline 3.1.2c (free trial toggle on the paywall + EULA link requirement). Primary language accidentally set to Danish, which cannot be reverted in App Store Connect.
Added English (U.S.) as the primary localized language. Filled the Danish fields with proper translations. Reconfigured the StoreKit trial flow. Wrote and shipped localization packages for 10 languages: ES, PT-BR, FR, DE, JA, IT, KO, AR, NL, TR.
Added English (U.S.) as the primary localized language. Filled the Danish fields with proper translations. Reconfigured the StoreKit trial flow. Wrote and shipped localization packages for 10 languages: ES, PT-BR, FR, DE, JA, IT, KO, AR, NL, TR.
Mes: AI Reply & Translate goes live. Weekly $1.99 · Monthly $5.99 · Yearly $39.99. Six sticker packs. Ten languages. Apple Design Award target maintained from concept through ship.
Mes: AI Reply & Translate goes live. Weekly $1.99 · Monthly $5.99 · Yearly $39.99. Six sticker packs. Ten languages. Apple Design Award target maintained from concept through ship.
In most AI products, the UI displays the model's output. In Mes, the UI gives the user control over the model's voice. That inversion is the whole case study.
What I expected to be the hard part, getting the Gemini API to return good replies, turned out to be the easy part. What I expected to be the easy part, making users want to send an AI-generated reply, turned out to be the entire problem. Every design decision in Mes routes back to that inversion.
I think it generalizes. In generative AI, agency is the trust mechanism, not accuracy. The user who steers the model trusts the result. The user who audits it doesn't. Mes is one expression of that pattern in a consumer surface. The same problem exists in every enterprise AI product I've used: workflows where the model acts on the user's behalf, and the user has no control over how. The pattern is the same. The fix is the same.
Building Mes alone (design, code, ship) with Claude Code as my engineering partner collapsed the design-engineering loop in a way I haven't experienced before. I'd sketch a tone-chip interaction in the morning and it would be in SwiftUI by lunch. The case study above is the design story. The deeper story is what becomes possible when the same person owns the decision and the implementation.














