QuickStop
Building a Convenience Store App That Actually Delivers on Convenience
Role
Product Designer + iOS Developer
Stack
Claude AI · Figma · Figma MCP · SwiftUI · UIKit · ConvexMobile · Stripe
The Problem Worth Solving
Convenience stores fail at the one thing in their name.
Lines behind lottery-ticket buyers. Mobile orders left on an open shelf anyone can grab. Fresh food with no prep timestamp. A drive across town only to find the item is out of stock. The apps that exist are loyalty cards with a receipt feature bolted on — they don't touch the actual friction.
The real problem isn't speed. It's certainty. Every customer arriving at a convenience store is making a bet: will the item be there, will the line be short, will this take 90 seconds or nine minutes? The opportunity was to eliminate the bet entirely — and let speed follow naturally once certainty was built in.
That single reframe — from fast to certain — drove every design and architecture decision that followed.
How This Was Built
This project ran as a deliberate end-to-end test of an AI-assisted product workflow, using Claude throughout: not as an autocomplete, but as a thinking partner that could hold the full problem space and move between strategy, design, and code in the same session.
The honest result: the gap between having a judgment and seeing it rendered essentially closed. What previously took days of iteration — user research synthesis, IA scaffolding, component spec, Swift boilerplate — compressed into hours. That freed up the real bottleneck: the design decisions that actually require a human.
Part I — Design
Finding the Five Mindsets
Before wireframes, the question was: who is actually using a convenience store, and what does “fast” mean to each of them?
Standard market segmentation produces demographics. What the app needed was jobs to be done — specific goals people arrive with at different times of day. Using Claude to synthesize behavioral patterns from real convenience store use cases, five personas emerged:
| # | Name | Archetype | Their version of "fast" |
|---|---|---|---|
| 01 | Marcus Hale, 32 | The Commuter | 90 seconds in-and-out, same order every day |
| 02 | Priya Ramanathan, 28 | The Night Shifter | Curbside at 3am, fresh food she can trust |
| 03 | Danny Reyes, 41 | The Bulk Buyer | Four coffees, four tacos, one transaction |
| 04 | Zoe Kapoor, 19 | The Impulse Local | Scan-and-go with room to browse; three taps max |
| 05 | Sarah Mitchell, 38 | The Emergency Errand | Delivery in under 15 minutes, verified stock |
Each persona produced a priority weighting across dimensions — Speed, Freshness, Reliability, Discovery — that became design decision criteria throughout the project. When a structural choice had to be made, the question was: which persona does this hurt, and can they afford to be hurt?
The key insight, visible across all five:they don't all want the same speed — they want certainty. Certainty that the item is there, that the pickup method will work, that the trip will take exactly as long as expected. Build for certainty and speed becomes a byproduct.
What Makes This Different
Most convenience store apps exist to capture loyalty points, not to solve the trip. The design difference here is that every screen is designed around a specific moment of uncertainty and resolves it before anything else:
- The home screen answers "is my store open and stocked?" before surfacing any action
- The shop screen surfaces real-time stock status, not a catalogue that might be fiction
- The orders screen is a live status board, not a receipt archive
- Age-gated categories tell the user about the ID check before checkout, not when the driver arrives
This is a different product philosophy than “make the app look nice.” Every UX decision is load-bearing.
Information Architecture — The Hard Structural Calls
The IA started with five obvious tabs: Home, Shop, Orders, Store, You.
Two structural decisions changed the outcome:
Decision 1 — Scan & Go gets its own tab.
Scan & Go was originally nested inside Shop as a mode toggle. It was promoted to the top-level tab bar because it's not a shopping mode — it's a mode of being in the store. The input model is fundamentally different (camera-first vs. browse/search), and Zoe's constraint — three taps or she abandons — is only satisfiable from the tab bar, not from inside a nested flow.
Decision 2 — Store merges into Home.
Promoting Scan meant the tab bar was now at six. Rather than a sixth tab, Store (hours, live inventory health, location) was dissolved into Home's smart header. This was actually a better outcome: instead of certainty living on a separate screen people have to navigate to, it became the first thing visible on the app's primary screen.
[ Home ] [ Shop ] [ Scan ] [ Orders ] [ You ]
| Tab | Core job | Serves |
|---|---|---|
| Home | Smart entry — reorder, store status, quick actions | Marcus, Sarah |
| Shop | Browse + order-ahead + delivery | Zoe, Priya, Sarah |
| Scan | Camera-first in-store checkout | Zoe, Marcus |
| Orders | Active tracking + pickup/delivery methods | All |
| You | Profile, payment, loyalty | All |
Home Screen
The home screen is Marcus's primary surface — but it has to work for Sarah's emergency errand moments too. The “The Usual” module is the first reorderable content after the live store header, keeping his 90-second exit achievable. Below it, the quick-action tiles give everyone a direct path to their preferred mode without browsing.
The store status card at the bottom — wait time, in-stock percentage, delivery ETA — is the certainty layer made visible. It answers the question every user has before they even leave the house.
Shop Taxonomy — Structure at Scale
Ten top-level categories. Two age-gated. Each with subcategories, special filters, and cross-cutting feature logic tied to specific personas. The structural rule was two levels maximum — Category → Subcategory → Product list — with plain-language names that match real convenience store terminology.
The Shop landing is the first screen where Zoe's browse instinct and Marcus's extract instinct have to coexist. The mode selector (Order Ahead / Delivery) sits at the top so intent is declared early. “The Usual” reorder card surfaces immediately below — Marcus can be done in two taps without touching the category grid at all. Zoe scrolls past it to browse.
The four cross-cutting strips that tie personas to the taxonomy
| Feature | Persona | Logic |
|---|---|---|
| "The Usual" | Marcus | History-driven one-tap reorder at top of Shop |
| "New This Week" | Zoe | Discovery strip; excludes age-gated categories |
| "Need It Now" | Sarah | Emergency shortcut preset; excludes age-gated |
| "Running Low?" | All | Cadence-based reorder prompt |
Age-gating model (v1 — trust-and-confirm)
The decision was a deliberate scope constraint. In-app ID scanning, ID.me, biometric verification — all deferred to v2. v1 uses a four-step model: DOB stored at account level → one-time-per-session cart acknowledgement → persistent ID-check banner on the Orders tab → manual visual ID check by driver or associate at handoff. The IA was deliberately designed so v2 can insert a real verification step between steps 3 and 4 without restructuring anything around it.
Category Page — Cold Drinks
The category page is where the certainty layer has to be most granular. A “79 items” count means nothing if 55 of them are warm. The “24 cold now” metadata next to the item count is the design doing its core job: answering the question before it's asked.
Stock status badges on individual cards (green “Cold”, amber “Low”, red “Out”) give Zoe the information she needs to make a decision in the aisle without asking staff. The featured banner surfaces the week's deal without pushing it — it's the first content block after the subcategory chips, not a full-screen takeover.
Onboarding
The onboarding flow has one job: get the user to a first order fast, and set the habit that brings them back. Six screens:
- Value prop— “4 minutes. Your order. Ready. Guaranteed.” — leading with the promise, not a feature list
- Location — map-first, showing real nearby stores with wait times before asking for permission
- Category preferences— 12-tile grid; pick what you usually buy; used to personalize “New this week”
- Sign in— deferred until after the user has seen the product's value
- First order — a pre-populated order (most popular combo) with a first-order discount applied; change it if you want
- Success + habit prompt— order placed, 4-minute ETA visible, “Make this your usual?” prompt to set a recurring schedule
The habit prompt on the final screen is the most important moment in the flow. If Marcus sets “Mon · Wed · Fri · 8:15am,” the app has already won retention before he's picked up his first order.
Orders Tab
Active order tracking is Priya's screen — she's in the car at 3am watching the ETA count down before she walks in. The progress bar (Placed → Preparing → Ready → Picked up) keeps the state legible at a glance. The “I'm here” button for curbside triggers the car-side handoff without her needing to go inside.
The persistent ID-check banner (not shown here, added at checkout for age-gated items) runs across active orders that include alcohol or tobacco — visible in Orders so there are no surprises at the door.
Scan & Go — Scanning
Scan & Go is Zoe's exit strategy and Marcus's shortcut when he's already in the store. The flow is deliberately minimal: camera fills the screen, item count and cart total are always visible, checkout is one tap from anywhere. There's no browse mode inside Scan — you're in the store, you're scanning, you're leaving.
The cart total bar at the bottom keeps the running price visible through the whole session, which also handles the common “wait, how much is this?” moment without breaking the scanning flow.
Part II — Development
Architecture Philosophy
The Swift project is organized around a principle that mirrors the Figma file structure: atomic design, strictly applied. The goal was to make the boundary between design and code mechanical rather than interpretive — a molecule in Figma maps to a molecule in Swift, no translation required.
Quickstop/ ├── Sources/ │ ├── App/ # Entry points │ ├── Components/ │ │ ├── Atoms/ # Primitive, single-purpose │ │ │ ├── DSBadge │ │ │ ├── DSButton │ │ │ ├── DSLabel │ │ │ ├── DSTextField │ │ │ └── DSToggle │ │ ├── Molecules/ # Composed from atoms │ │ │ ├── ProductCard │ │ │ ├── CartItemRow │ │ │ ├── StockBadge │ │ │ └── SearchBar │ │ └── Organisms/ # Full UI sections │ │ ├── ProductGrid │ │ ├── CartSummary │ │ └── PromoBannerCarousel │ ├── DesignSystem/ │ │ └── Tokens/ │ │ ├── DSColors │ │ ├── DSTypography │ │ ├── DSSpacing │ │ ├── DSRadius │ │ └── DSShadow │ ├── Features/ │ │ ├── Home/ │ │ ├── Cart/ │ │ ├── Orders/ │ │ ├── Auth/ │ │ └── Search/ │ └── ViewModels/
Design System in Code
Before a single screen was built, the token layer was established. The brand palette was extracted directly from the Figma file via Figma MCP — variable values pulled programmatically, not transcribed by hand — and exported as DSColors.swift.
Brand palette
brandPrimary
Teal · #30C5FF
brandAccent1
Forest · #5C946E
brandAccent12
Sage · #80C2AF
brandAccent13
Sky · #A0DDE6
The file covers 70+ tokens across brand colors, system backgrounds (primary/secondary/tertiary with elevated variants), grouped backgrounds, labels, and vibrant labels — all with Light/Dark mode values, all accessible via Color.brandPrimary or UIColor.backgroundSecondary in view code.
The result: no hardcoded hex values anywhere in component or feature code. Light/Dark mode is handled entirely at the token layer — no conditional appearance checks in view logic.
// Every color decision in the codebase looks like this:
Text("Welcome")
.foregroundColor(.labelPrimary)
.background(Color.backgroundSecondary)
Button("Order Now") { }
.tint(.brandPrimary)Component Tiers
Atoms
Atoms wrap iOS system controls with design system tokens applied. DSButton applies brand colors and the correct type scale. DSLabel applies semantic label colors. No business logic lives here.
Molecules
Molecules compose atoms into product concepts. ProductCard combines DSLabel, PriceLabel, StockBadge, and DSButton into one reusable unit. CartItemRow composes quantity, price, and image into a list-ready component.
Organisms
Organisms assemble molecules into full UI sections. ProductGrid handles layout and state for a list of ProductCards. PromoBannerCarousel manages scroll state across multiple banners.
The division is enforced: no business logic in atoms or molecules, no token lookups in organisms (they consume components, not token values directly).
Key Package Dependencies
ConvexMobile 0.7.0
Real-time backend sync for live inventory, order status, and stock levels. This is the technical choice that directly delivers the product promise. Certainty about what's in stock is only possible with a real-time data layer; polling doesn't cut it for a user who's already in the car.
Stripe 23.32.0
Payment processing. Apple Pay integration via the Stripe SDK keeps checkout friction minimal for Marcus's 90-second exit.
AI-Assisted Development with Cursor
The Swift implementation was built using Cursor with DSColors.swift, DSTypography, and the project structure as persistent context. The workflow that changed most:
Component scaffolding
Cursor generated atom and molecule boilerplate from a description of the Figma equivalent, with correct tokens applied from the design system files already in context. Starting a new component meant describing it, not writing it from scratch.
Token referencing
Rather than looking up hex values or sizing constants, Cursor resolved the right token for each use case from the existing token files. No design-to-code translation step.
Preview generation
PreviewMocks stubs were generated alongside each component so SwiftUI previews worked immediately, without a live backend. No waiting to see what a component looks like.
Bulk refactoring
When token names changed during design system iteration, Cursor propagated the rename across all component files in a single pass.
What AI Actually Changed
This project was a deliberate test of how far an AI-assisted workflow could go end-to-end. The honest accounting:
What compressed significantly
| Task | Before | With AI |
|---|---|---|
| Persona synthesis | Days of research consolidation | One focused session |
| IA iteration | Multiple async review rounds | Propose, critique, revise in the same session |
| Shop taxonomy + edge cases | Multiple doc drafts | Single iterative session with immediate stress-testing |
| DSColors.swift | Manual Figma → Swift transcription | Extracted programmatically via Figma MCP |
| Figma screen construction | Hand-placed every element | Plugin code written and executed via MCP |
| Swift component boilerplate | Written from scratch | Scaffolded from Figma description + token context |
What still required human judgment
- Deciding which structural option was right — Scan & Go promotion, merging Store into Home
- Visual taste — verifying that designs looked right, not just structurally correct
- Scoping — what goes in v1, what waits for v2, and what never ships
- Architecture decisions — the atomic design hierarchy, token layer separation, ConvexMobile choice
The net effect: AI removed the time between having a judgment and seeing it rendered. It didn't produce the judgments.
Deliverables
| Artifact | Format | Description |
|---|---|---|
| personas.html | HTML | Five user personas with pain points, needs, priority bars |
| quickstop-ia.md | Markdown | Full IA v0.4, 5-tab nav, age-gating model |
| quickstop-shop-categories.md | Markdown | Shop taxonomy v0.4, 10 categories, age-gated flows |
| DSColors.swift | Swift | Full color token file, SwiftUI + UIKit, 70+ tokens |
| Figma design file | Figma | Shop Landing, Cold Drinks, Product Detail, Onboarding (6 screens) |
| iOS implementation | Swift | Native app using atomic design + DSColors token bridge |
What's Next
- →Age-gated checkpoint sheet (the ID-required modal at cart level)
- →Orders tab — active order tracking, pickup method screens
- →Scan & Go flow — scanner UI, cart drawer, exit & pay
- →v2 scoping: crew/group ordering, business accounts, in-app ID verification (drop-in to existing flow)