Practice spoken English, one sentence at a time.
SpeakEasy is a SwiftUI app that uses Apple's new SpeechAnalyzer API (iOS 26) to score what the recogniser heard against French→English practice sentences in real time, with phonetic tolerance for common French-speaker pronunciation.
- Listen — Hear the English sentence read aloud before you speak
- Record — Speak the sentence; progressive transcription streams as you talk
- Score — Per-attempt score (0–100) with token-by-token feedback (correct / missing / incorrect / extra)
- Progress — Tracks today's attempts, completed sentences, and difficult ones to review
- Settings — Adjustable session size
- Xcode 17+ (Apple SpeechAnalyzer SDK ships with Xcode 26 toolchain)
- iOS 26.0+ device or simulator
- Microphone and Speech Recognition permissions
git clone git@github.com:S1933/SpeakEasy.git
cd SpeakEasy
open SpeakEasy.xcodeprojBuild and run on a real device for best results — the simulator has no real microphone and the new SpeechTranscriber assets may not be installed.
Required Info.plist keys (already configured):
NSMicrophoneUsageDescription— "SpeakEasy uses the microphone so you can practice spoken English."NSSpeechRecognitionUsageDescription— "SpeakEasy uses speech recognition to compare what you say with the practice sentence."
SpeakEasy/
├── Features/
│ ├── Home/ Stats + Start CTA
│ ├── Practice/ Recording flow + per-sentence view
│ ├── Settings/ Session size
│ └── Summary/ End-of-session recap
├── Services/
│ ├── Speech/ SpeechRecognitionService, AudioSession, Permissions
│ ├── Scoring/ SentenceScoringService (token-level diff via Levenshtein-like DP)
│ └── Progress/ SwiftData persistence
├── Models/ LearningSentence, AttemptResult, etc.
├── Data/ sentences.json, SentenceRepository
└── Shared/
└── Components/ MicrophoneButton, SentenceRow, etc.
Key technical decisions:
- SpeechAnalyzer pipeline — uses
SpeechAnalyzer+SpeechTranscriberwith the.progressiveTranscriptionpreset for streaming results. Audio is captured viaAVAudioEnginetap at the hardware format (typically 48 kHz Float32), then converted per-buffer to 16 kHz Int16 mono interleaved. A single reusableAVAudioConverter(with.noDataNowon dry input) is kept across callbacks — no allocation on the real-time audio thread. - Concurrency —
@Observable+@MainActor;SpeechRecognitionServiceis main-actor isolated; long-running work (analyzer.start(inputSequence:), result collection) is dispatched as unstructuredTasks with explicit error handling. - Persistence — SwiftData with two models:
SentenceProgress(per-sentence score history) andAppSettings(session size).
xcodebuild test -project SpeakEasy.xcodeproj -scheme SpeakEasy \
-destination 'platform=iOS Simulator,name=iPhone 16'24 unit tests cover scoring, normalization, and feedback.
Private project — all rights reserved.