Skip to content

tsushanth/ParrotCompanion

Repository files navigation

Parrot Companion

A working SwiftUI prototype of four shipping-ready iOS features for Parrot (YC F25): a hands-free audio review mode, an on-device pronunciation drill, a Home Screen widget, and a Lock Screen Live Activity.

Parrot Companion home screen showing four feature rows

Built unsolicited as a tangible answer to the open Founding Mobile Engineer posting. Each feature is wired against a real gap I found in App Store reviews or the public product surface — not invented from scratch.


Why these four features

The current Parrot daily loop is screen-bound: morning video, lunch repeat-aloud, evening review. The features below extend the loop to commute time, lock screen, and home screen — surfaces where engagement compounds streaks without asking for more thumb-time.

Feature Gap it closes Evidence
Hands-free audio review No way to practice while walking / driving / dishes — biggest unmet use case for a comprehensible-input app once vocab exists Pimsleur owns this segment; Duolingo doesn't. Parrot's video format precludes it today.
On-device pronunciation drill "Parrot-back" today is honor-system. No objective signal whether a learner is matching the target. App Store review patterns; Krashen's i+1 needs output feedback to advance.
Home Screen widget (3 sizes + Lock Screen accessory) No widget today. Daily-streak apps lose retention without a passive surface. App Store review "Widget for iPhone home screen" by Zoievande18 (May 4) — explicit request.
Live Activity + Dynamic Island Streak/next-review state is invisible until you open the app. Same Zoievande18 request implicitly + general iOS 16+ user expectation.

What's in the box

ParrotCompanion/
├── ParrotCompanion/            # Main app target
│   ├── ParrotCompanionApp.swift
│   ├── Models/
│   │   ├── Phrase.swift             # Codable phrase + demo seed
│   │   └── PhraseStore.swift        # Leitner SRS, App Group-backed
│   ├── Audio/
│   │   ├── AudioReviewEngine.swift  # AVSpeechSynthesizer + MPNowPlayingInfo
│   │   └── PronunciationScorer.swift # SFSpeechRecognizer + Levenshtein scoring
│   └── Views/
│       ├── ContentView.swift
│       ├── ReviewSessionView.swift  # Audio player UI
│       └── PronunciationDrillView.swift
├── ParrotWidget/               # WidgetKit + Live Activity extension
│   └── ParrotWidget.swift           # Widget + ActivityConfiguration + DynamicIsland
└── ParrotLiveActivity/         # Shared between app + extension
    └── StreakAttributes.swift

Audio Review Engine — AudioReviewEngine.swift

  • AVSpeechSynthesizer with es-MX and en-US voices, rate tuned 0.92x default
  • AVAudioSession configured for .playback / .spokenAudio with .duckOthers — keeps speaking through screen lock, ducks Spotify
  • Now Playing center wired so the lock screen / CarPlay shows current phrase as track title, English meaning as artist, "Parrot Review — 4/12" as album
  • Remote command center handles play / pause / next from AirPods stem + CarPlay buttons
  • Phrase loop: Spanish → 1.2s gap → English → Spanish repeat → record SRS pass → next

Pronunciation Scorer — PronunciationScorer.swift

  • SFSpeechRecognizer(locale: es-MX) with requiresOnDeviceRecognition opt-in when supported
  • Live partial transcripts during listening
  • Blended score: 55% Levenshtein on normalized strings + 45% word-overlap (diacritic-insensitive, punctuation-stripped). Survives the common "¿" / accent-mark drops
  • 0-100 score gates the SRS box decision (≥70 → promote; <70 → demote)

Swap point: if you want phoneme-level prosody scoring, the similarity(_:target:) static is the only place to replace — Azure Cognitive Services / Whisper-based scoring drops in cleanly.

Widget — ParrotWidget.swift

  • Five families: systemSmall, systemMedium, systemLarge, accessoryRectangular, accessoryInline
  • Reads shared PhraseStore via App Group; refreshes every 30 min
  • Each size shows: today's Spanish phrase, English meaning, current streak, minutes-until-next-review
  • Tap-through deeplinks into the app (the widgetURL is left as an integration point — wire to your existing universal link router)

Live Activity — StreakLiveActivity

  • Lock Screen: 🦜 + next phrase + streak + countdown
  • Dynamic Island compact: parrot + streak day count
  • Dynamic Island expanded: phrase, streak, minutes-to-next
  • Stale-date 2h; refresh on session completion

Integration with Parrot's existing stack

These are the seams to swap from the demo's in-memory data to real Parrot data:

  1. PhraseStore — replace seedIfEmpty() and phrases getter with a fetch from Parrot's user vocab endpoint. Keep the App Group write-through so the widget/Live Activity stay accurate.
  2. PhraseStore.record(_:recalled:) — currently writes to UserDefaults. Replace body with a POST to your SRS endpoint; keep the local optimistic write for offline.
  3. AudioReviewEngine.say(_:lang:) — uses AVSpeechSynthesizer so it's $0 and runs offline. If you want native-speaker audio that matches your videos, replace the utterance creation with a clip lookup against phrase.videoClipID and play extracted audio via AVAudioPlayer. The Now-Playing wiring stays.
  4. ParrotWidget.getTimeline — uses 30-min refresh. Once you wire a server-side push of "due now" counts via background refresh, you can move to .never policy and refresh on push.
  5. Streak source of truth — currently local. Swap markPracticedToday() to call your server.

Required Xcode capabilities

  • App Groupsgroup.com.parrotapp.companion enabled on both main app + widget extension target
  • Background Modes — Audio (for hands-free playback)
  • Push Notifications — only if you want server-pushed Live Activity updates later
  • Info.plist keys on main app:
    • NSMicrophoneUsageDescription — "We listen so you can hear how close your pronunciation is."
    • NSSpeechRecognitionUsageDescription — "Speech recognition runs on-device to score your pronunciation."

See docs/PROJECT_SETUP.md for the 5-minute Xcode wire-up.


What I'd validate next

A few things I'd want to instrument before claiming any of this lifted retention:

  • Streak D7 delta for users who add the widget vs. matched users who don't (Mixpanel cohort)
  • Session count per user/day: audio mode is additive — we'd want to see total daily sessions ↑, not video sessions ↓ (cannibalization risk)
  • Pronunciation score → next-day return correlation — if drill users return more often, that's the active-recall flywheel Krashen's framework predicts but Parrot doesn't measure today
  • A/B: replace AVSpeechSynthesizer voice with a real native-speaker clip cut from the existing video library — does same-voice consistency improve adherence?

What I didn't build (would, given a day or two more)

  • Apple Watch companionWKInterfaceController to skip/replay during runs; Watch already has the AirPods stem so the value is "see the phrase + heart-rate context"
  • Spotify-style "Daily Spanish" auto-generated podcast — concatenate the day's phrases into a sharable audio file with chapters; ties into the YC "TikTok for X" angle by making Parrot shareable on Discord/iMessage as audio clips
  • iMessage extension — send-a-phrase as a shareable sticker pack to friends learning together
  • Voice-cloned native-speaker variants — same phrase rotated through a few cloned voices (varied accents) to prevent vocabulary boredom (#3 review complaint: "same phrases over and over")

Built by Sushanth Tiruvaipati. Reach me at t.sushanth@gmail.com.

About

iOS prototype: hands-free audio review, on-device pronunciation drill, Home Screen widget, and Lock Screen Live Activity — built for Parrot (YC F25).

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages