A working SwiftUI prototype of three shipping-ready iOS surfaces for Sunflower (YC F25): a hands-free "Whisper to Sam" voice mode for late-night urges, a Lock Screen Live Activity for the sober day count, and a Home Screen widget with milestone progress.
Built unsolicited. Each surface maps to a specific pattern across the 5-star App Store reviews — not a guess at what users might want.
The current Sunflower iOS app does the heavy lifting beautifully (Sam, day counter, journal). The gaps users implicitly reach for are off-screen, not in-screen:
| Surface | Gap it closes | Evidence |
|---|---|---|
| Whisper to Sam (hands-free voice mode) | "Late-night when I can't talk to humans, I open Sam" — but text-chatting in bed with shaky hands is exactly the wrong UX for a peak urge. Voice is. | Apple_Ann08: "AI chat for late-night crisis support when unable to contact others." Northwest Darling, snicklelove, Carxxsatf200 all cite the same 24/7-late-night use case. |
| Lock Screen Live Activity (sober days + days to next milestone) | Reviewers obsess over the day counter, but checking it requires opening the app. A glance on the lock screen turns the streak from a destination into ambient reinforcement. | JonDA., brooklynbabe718, Highley92 all describe the counter as their primary motivator. None mention a widget exists — because none does yet. |
| Home Screen widget (3 sizes + Lock Screen accessory) | Same — passive visibility, no taps needed. Sober-day apps live or die on streak salience. | Same review cluster. |
The fourth surface (a 60-second 5-4-3-2-1 grounding flow for sub-Sam urges) is also included as a lower-friction alternative when the user doesn't have AirPods in or the moment is too brief to start a conversation.
SunflowerCompanion/
├── SunflowerCompanion/ # Main app target
│ ├── SunflowerCompanionApp.swift
│ ├── Models/
│ │ ├── SobrietyState.swift # Milestone ladder + UrgeEvent
│ │ └── SobrietyStore.swift # App-Group-backed streak state
│ ├── Audio/
│ │ └── WhisperToSamEngine.swift # SFSpeechRecognizer + AVSpeechSynthesizer voice loop
│ ├── Intelligence/
│ │ └── SamResponder.swift # Local mock + Remote stub (single integration point)
│ └── Views/
│ ├── ContentView.swift # Home
│ ├── WhisperToSamView.swift # Voice mode UI
│ └── GroundingView.swift # 60-second 5-4-3-2-1 flow
├── SunflowerWidget/ # WidgetKit + Live Activity extension
│ └── SunflowerWidget.swift # 6 widget families + Live Activity + Dynamic Island
└── SunflowerLiveActivity/ # Shared attrs between app + extension
└── SobrietyAttributes.swift
- Tap once → loop runs hands-free. Designed for AirPods + locked phone on the nightstand.
AVAudioSessionconfigured for.playAndRecord/.voiceChatso it ducks music and routes through AirPods.SFSpeechRecognizerwith on-device opt-in when supported. End-of-turn detected on 1.4s of silence after the most recent partial transcript update.AVSpeechSynthesizerfor Sam's voice, slightly slowed (0.95×) and pitched up subtly so it doesn't read as robotic.- Auto-continues — after Sam finishes speaking, the engine flips back to listening without user input. Turn count tracked so the responder can shape replies differently in the opening vs. mid-conversation.
Swap point for Sunflower's existing Sam pipeline: SamResponder.swift has a RemoteSamResponder stub. Drop your endpoint URL + bearer token there and the rest of the engine works unchanged. Production replies use the same prompt + the same physician-reviewed eval suite you already run; nothing about the voice surface forces a model change.
- Lock Screen: 🌻 + "47 days sober" + "→ Two months in 13"
- Dynamic Island compact: 🌻 + day count
- Dynamic Island expanded: day count, current milestone target, and days remaining
- Stale-date 24h (the day count is the only thing that needs to advance, and it only advances at midnight)
systemSmall / systemMedium / systemLarge (Home Screen), plus accessoryRectangular, accessoryCircular, accessoryInline (Lock Screen + StandBy). Refresh policy is pinned to local midnight so the day count flips on time without burning timeline budget.
The lower-friction sibling to Whisper to Sam. 60-second 5-4-3-2-1 walkthrough, no audio, single-thumb. Logs an UrgeEvent on completion so the home screen can show "your urges pass in 9 minutes (median)" — the data point that fights the "but this one feels different" cognitive distortion.
SobrietyStore— replace thestartDateseed with a fetch from your user-state endpoint. Keep the App-Group write-through so the widget + Live Activity stay accurate.SamResponder— swapLocalSamResponderforRemoteSamResponderpointed at your Sam endpoint. The endpoint contract in the stub isPOST { user_text, turn_index, surface: "voice" } → { reply }— adapt as needed.Activity<SobrietyAttributes>updates — when the user hits a new milestone, push an update to the Live Activity to flip the next-milestone label. Helper not included; happy to add as a follow-up.- Anthropic Claude usage —
SamResponderis model-agnostic on purpose. Same Claude prompt, same evals.
- App Groups —
group.sunflowersober.companion(change to your team's group) on both targets - Background Modes → Audio (for the Whisper-to-Sam loop with phone locked)
- Info.plist keys (already set in
project.yml):NSMicrophoneUsageDescriptionNSSpeechRecognitionUsageDescriptionNSSupportsLiveActivities
Generate the .xcodeproj with xcodegen generate. Built clean on Xcode 26.4.1 / iOS 17 deployment target.
A few cohort comparisons I'd want before this gets put behind a flag for everyone:
- D7 / D30 retention for users who add the widget vs. matched users who don't. Streak-app conventional wisdom is that widget installers retain ~2x.
- Sam conversation count per user per week — does voice mode cannibalize text, or add net new sessions? Hypothesis: net new — voice happens in moments text doesn't (driving, walking, in bed with lights off).
- Urge-event log rate — does the grounding flow get used? Time-to-pass median per intensity bucket is the data point that fights the cognitive distortion behind a relapse.
- Conversion to paid — voice mode is a natural premium gate (compute + Claude tokens cost more per minute than chat). Hypothesis: voice mode lifts trial→paid conversion by 30%+. Worth A/B-testing as Premium-only after a free first session.
- Apple Watch app — would extend the Live Activity to the wrist (
WKInterfaceController+ Workout-style heart-rate context). Smaller scope than it sounds; happy to add. - Haptic-only urge response — Apple Watch crown haptic taps matching breath cadence, for moments when the user can't make audio.
- Voice-cloned Sam — pick from a small set of voices (parent, sponsor, "future you"). I run a voice-cloning service (OpenVoice v2) that drops in here; intentionally left out so this PR is reviewable as plain SwiftUI.
Built by Sushanth Tiruvaipati. Reach me at t.sushanth@gmail.com.
