$npx -y skills add dpearson2699/swift-ios-skills --skill speech-recognitionTranscribe speech to text using Apple's Speech framework. Use when implementing live microphone transcription with AVAudioEngine, recognizing recorded audio files, handling speech and microphone authorization, choosing on-device vs server-backed SFSpeechRecognizer behavior, or ad
| 1 | # Speech Recognition |
| 2 | |
| 3 | Transcribe live and pre-recorded audio to text using Apple's Speech framework. |
| 4 | Covers `SpeechAnalyzer` / `SpeechTranscriber` (iOS 26+) and |
| 5 | `SFSpeechRecognizer` (iOS 10+) fallback guidance. |
| 6 | |
| 7 | **Scope boundary:** Use this skill for speech-to-text recognition, speech |
| 8 | authorization, microphone capture plumbing, and result handling. Hand off text |
| 9 | analysis, language identification after transcription, sentiment, embeddings, |
| 10 | and translation to `natural-language`; hand off audio playback UI to `avkit`; |
| 11 | hand off summarization or generation over transcripts to `apple-on-device-ai`. |
| 12 | |
| 13 | ## Contents |
| 14 | |
| 15 | - [SpeechAnalyzer Strategy (iOS 26+)](#speechanalyzer-strategy-ios-26) |
| 16 | - [SFSpeechRecognizer Setup](#sfspeechrecognizer-setup) |
| 17 | - [Authorization](#authorization) |
| 18 | - [Live Microphone Transcription](#live-microphone-transcription) |
| 19 | - [Pre-Recorded Audio File Recognition](#pre-recorded-audio-file-recognition) |
| 20 | - [On-Device vs Server Recognition](#on-device-vs-server-recognition) |
| 21 | - [Handling Results](#handling-results) |
| 22 | - [Common Mistakes](#common-mistakes) |
| 23 | - [Review Checklist](#review-checklist) |
| 24 | - [References](#references) |
| 25 | |
| 26 | ## SpeechAnalyzer Strategy (iOS 26+) |
| 27 | |
| 28 | Use `SpeechAnalyzer` for modern iOS 26+ speech analysis, especially long-form |
| 29 | recordings, live transcription, time-indexed transcripts, and fully on-device |
| 30 | flows. Keep `SFSpeechRecognizer` for iOS 10+ deployment targets, server-backed |
| 31 | locale coverage, or existing callback/delegate implementations. |
| 32 | |
| 33 | Read [SpeechAnalyzer patterns](references/speechanalyzer-patterns.md) when |
| 34 | implementing an iOS 26+ transcription pipeline, model asset handling, volatile |
| 35 | results, or file/buffer examples. |
| 36 | |
| 37 | ### SpeechAnalyzer setup checklist |
| 38 | |
| 39 | 1. Choose the module: |
| 40 | - `SpeechTranscriber` for the newer general-purpose on-device model. |
| 41 | - `DictationTranscriber` when `SpeechTranscriber` is unavailable for the |
| 42 | current device or locale and dictation-compatible support is acceptable. |
| 43 | - `SpeechDetector` only in conjunction with a transcriber when voice |
| 44 | activity detection is worth the accuracy/power tradeoff. |
| 45 | 2. Check support before creating the session: |
| 46 | - `SpeechTranscriber.isAvailable` |
| 47 | - `SpeechTranscriber.supportedLocale(equivalentTo:)` |
| 48 | - `SpeechTranscriber.installedLocales` / `supportedLocales` when showing |
| 49 | language choices. |
| 50 | 3. Pick a documented preset: |
| 51 | - `.transcription` for basic accurate transcription. |
| 52 | - `.progressiveTranscription` for live UI updates. |
| 53 | - `.timeIndexedProgressiveTranscription` when playback highlighting needs |
| 54 | `audioTimeRange`. |
| 55 | 4. Install required assets with `AssetInventory.assetInstallationRequest`. |
| 56 | 5. Convert live audio buffers to |
| 57 | `SpeechAnalyzer.bestAvailableAudioFormat(compatibleWith:)` before yielding |
| 58 | `AnalyzerInput`. |
| 59 | 6. Consume module results from their `AsyncSequence` in a separate task. |
| 60 | 7. Finish explicitly with `finalizeAndFinish(through:)`, |
| 61 | `finalizeAndFinishThroughEndOfInput()`, or `cancelAndFinishNow()`. |
| 62 | |
| 63 | Do not use an `offlineTranscription` preset; Apple does not document one. |
| 64 | Finishing an `AsyncStream` input sequence does not finish the analyzer session. |
| 65 | |
| 66 | ## SFSpeechRecognizer Setup |
| 67 | |
| 68 | ### Creating a recognizer with locale |
| 69 | |
| 70 | ```swift |
| 71 | import Speech |
| 72 | |
| 73 | // Default locale (user's current language) |
| 74 | let recognizer = SFSpeechRecognizer() |
| 75 | |
| 76 | // Specific locale |
| 77 | let recognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US")) |
| 78 | |
| 79 | // Check if recognition is available for this locale |
| 80 | guard let recognizer, recognizer.isAvailable else { |
| 81 | print("Speech recognition not available") |
| 82 | return |
| 83 | } |
| 84 | ``` |
| 85 | |
| 86 | ### Monitoring availability changes |
| 87 | |
| 88 | ```swift |
| 89 | final class SpeechManager: NSObject, SFSpeechRecognizerDelegate { |
| 90 | private let recognizer = SFSpeechRecognizer()! |
| 91 | |
| 92 | override init() { |
| 93 | super.init() |
| 94 | recognizer.delegate = self |
| 95 | } |
| 96 | |
| 97 | func speechRecognizer( |
| 98 | _ speechRecognizer: SFSpeechRecognizer, |
| 99 | availabilityDidChange available: Bool |
| 100 | ) { |
| 101 | // Update UI — disable record button when unavailable |
| 102 | } |
| 103 | } |
| 104 | ``` |
| 105 | |
| 106 | ## Authorization |
| 107 | |
| 108 | Request **both** speech recognition and microphone permissions before starting |
| 109 | live transcription. Add these keys to `Info.plist`: |
| 110 | |
| 111 | - `NSSpeechRecognitionUsageDescription` |
| 112 | - `NSMicrophoneUsageDescription` |
| 113 | |
| 114 | ```swift |
| 115 | import Speech |
| 116 | import AVFoundation |
| 117 | |
| 118 | func requestPermissions() async -> Bool { |
| 119 | let speechStatus = await withCheckedContinuation { continuation in |
| 120 | SFSpeechRecognizer.requestAuthorization { status in |
| 121 | continuation.resume(returning: status) |
| 122 | } |
| 123 | } |
| 124 | guard speechStatus == .authorize |