Voice-to-text: wire existing mic button to SFSpeechRecognizer #23

Closed
opened 2026-06-24 18:10:36 +00:00 by kutesir · 1 comment
Owner

Completed

Delivered 2026-06-24

Problem

The mic icon in AddTaskSheet (Quick Add) was a bare Image(systemName: "mic") — no Button wrapper, no action, no speech infrastructure.

Implementation

New file: KisaniCal/Managers/SpeechRecognizer.swift

  • @MainActor ObservableObject wrapping SFSpeechRecognizer + AVAudioEngine
  • Requests speech → microphone permission in sequence
  • Streams partial results into @Published transcript
  • Suppresses error 301 (no speech) — not surfaced to user
  • reset() cleans up on sheet dismiss
  • [VOICE] log trail: tapped → granted → started → transcript → saved

TodayView.swift — AddTaskSheet

  • @StateObject private var speech = SpeechRecognizer()
  • onChange(speech.transcript) writes to rawText (task field)
  • ImageButton { speech.toggle() } with mic.fill + pulse animation
  • .safeAreaInset red error banner on permission denial
  • submit() stops recording before saving
  • .onDisappear { speech.reset() }

project.yml

  • NSMicrophoneUsageDescription
  • NSSpeechRecognitionUsageDescription

Verified

  • BUILD SUCCEEDED
  • App launches without crash on iPhone 17 simulator
  • Live audio test requires physical device (sign-in gate blocks simulator)
## Completed ✅ **Delivered 2026-06-24** ### Problem The mic icon in AddTaskSheet (Quick Add) was a bare `Image(systemName: "mic")` — no Button wrapper, no action, no speech infrastructure. ### Implementation **New file: `KisaniCal/Managers/SpeechRecognizer.swift`** - `@MainActor ObservableObject` wrapping `SFSpeechRecognizer` + `AVAudioEngine` - Requests speech → microphone permission in sequence - Streams partial results into `@Published transcript` - Suppresses error 301 (no speech) — not surfaced to user - `reset()` cleans up on sheet dismiss - `[VOICE]` log trail: tapped → granted → started → transcript → saved **`TodayView.swift` — AddTaskSheet** - `@StateObject private var speech = SpeechRecognizer()` - `onChange(speech.transcript)` writes to `rawText` (task field) - `Image` → `Button { speech.toggle() }` with `mic.fill` + pulse animation - `.safeAreaInset` red error banner on permission denial - `submit()` stops recording before saving - `.onDisappear { speech.reset() }` **`project.yml`** - `NSMicrophoneUsageDescription` - `NSSpeechRecognitionUsageDescription` ### Verified - BUILD SUCCEEDED ✅ - App launches without crash on iPhone 17 simulator ✅ - Live audio test requires physical device (sign-in gate blocks simulator)
Author
Owner

i am okay with this function in test

i am okay with this function in test
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kutesir/KisaniCal#23
No description provided.