iOS 26 SpeechAnalyzer Live Mic: Setup, Results, and Troubleshooting
For a live iOS 26 microphone pipeline, check device and locale support, prepare model assets, obtain a compatible PCM format, and consume recognition results while feeding audio. Treat normal completion separately from cancellation so the last words can become final. This guide follows Apple’s API documentation and the current SimpleMemo live-mic sample; it also includes a separately downloadable preparation helper.
Start with the complete sample, then inspect the pipeline
The MIT-licensed repository exposes SpeechSession as a Swift package for an iOS 26 host app. Add the repository under Xcode’s package dependencies and link the SpeechAnalyzerLiveMic product. Its README shows a SwiftUI view that displays finalizedText, renders volatileText separately, and disables Start while the session is preparing.
For a complete app project, follow the README’s XcodeGen route. Alternatively, copy the supplied app sources into a new iOS 26 app. Use one integration route so you do not define the same types twice. The package does not implement note storage, email delivery, or Obsidian integration.
The linked sample requests microphone and speech-recognition authorization. Its host app must supply NSMicrophoneUsageDescription and NSSpeechRecognitionUsageDescription; adding the package cannot supply those host descriptions. Explain permission use, show denial and preparation states, and let the user choose when to record. The helper below does not request permission or start the microphone.
Apple’s official live-transcription sample and WWDC25 session are useful references for the architecture. SpeechAnalyzer coordinates modules; it is an additional API for supported systems, not a reason to assume all existing SFSpeechRecognizer code is obsolete.
Prepare support, assets, and format before recording
SpeechTranscriber provides runtime device and locale checks. An iOS version check alone is insufficient. The following helper returns a configured transcriber, analyzer, and audio format, or throws a specific preparation error.
Download the complete Swift preparation helper. The full file includes imports, result and error types, and the DictationTranscriber hint helper discussed below. This is a setup component, not a complete recording app.
@available(iOS 26.0, *)
@MainActor
public func prepareSpeechAnalyzer(
requestedLocale: Locale,
naturalFormat: AVAudioFormat? = nil
) async throws -> PreparedSpeechAnalyzer {
guard SpeechTranscriber.isAvailable else {
throw SpeechPreparationError.deviceUnavailable
}
guard let locale = await SpeechTranscriber.supportedLocale(
equivalentTo: requestedLocale
) else {
throw SpeechPreparationError.unsupportedLocale
}
let transcriber = SpeechTranscriber(
locale: locale,
preset: .progressiveTranscription
)
// The system may already have the asset; nil means no install is needed.
// A request can automatically reserve the locale or throw if no slot is free.
if let request = try await AssetInventory.assetInstallationRequest(
supporting: [transcriber]
) {
try await request.downloadAndInstall()
}
try Task.checkCancellation()
guard await AssetInventory.status(forModules: [transcriber]) == .installed else {
throw SpeechPreparationError.assetsNotInstalled
}
guard let format = await SpeechAnalyzer.bestAvailableAudioFormat(
compatibleWith: [transcriber], considering: naturalFormat
) else {
throw SpeechPreparationError.noCompatibleFormat
}
let analyzer = SpeechAnalyzer(modules: [transcriber])
try await analyzer.prepareToAnalyze(in: format)
return PreparedSpeechAnalyzer(
locale: locale, transcriber: transcriber,
analyzer: analyzer, audioFormat: format
)
}
AssetInventory manages shared system models. A model may already be installed by the system or another app. An installation request returns nil when no installation is needed; otherwise it can reserve the locale and initiate a download. Reservation limits can also produce an error. Manage reservations across your app’s features, and release a locale when it is no longer needed rather than releasing a model still in use elsewhere in your app.
Before going offline, prepare the required locale and verify readiness. Model assets live outside the app bundle, but still consume system storage and can change over time. Show download progress and cancellation in the host UI. “On-device recognition” does not mean “no preparation or network access is ever needed.”
Feed PCM in the format the analyzer accepts
AVAudioEngine microphone tap
-> owned PCM / conversion for the selected format
-> AsyncStream<AnalyzerInput>
-> SpeechAnalyzer + SpeechTranscriber
-> result consumer and UI state
After activating the audio session, inspect the input node’s actual format. Do not hard-code a sample rate because a device or route often used that rate before. Pass a natural format to bestAvailableAudioFormat(compatibleWith:considering:) when appropriate and unwrap its optional result. Apple documents that the analyzer does not transparently resample or convert input.
The repository’s AudioBufferConverter returns an already matching buffer unchanged and uses AVAudioConverter when formats differ. Conversion is required for mismatched formats, not as a blanket extra step for every buffer. Check output frame length and report conversion errors; silently dropping every failing buffer can make a recording UI appear healthy while producing no text.
Give queued audio an explicit ownership policy. The updated sample converter returns independent PCM storage even when formats already match, so later reuse of the input does not overwrite queued audio. A buffer supplied by an audio callback must not be assumed to remain immutable after the callback returns. If you queue it for later use, arrange a suitable owned copy or a pipeline whose buffer lifetime is controlled. Keep per-buffer UI mutations off the audio callback. For production, also decide how you bound queued work and handle overload or route changes.
In Swift 6, capturing local variables does not by itself prove that a pipeline is thread-safe. The current sample’s converter has a single-processing-thread contract and a private lock-protected @unchecked Sendable holder for one converter callback. It does not make all PCM buffers or the converter generally safe to share across threads. Read that contract before adapting the sample.
Replace volatile results; finish input before finalizing
With progressive results enabled, a volatile result can revise text for an audio range. Do not append every partial result as a new sentence. The simple sample keeps finalized text and a replaceable volatile tail. For richer editing, align updates with the audio ranges and preserve final segments separately. Apple describes this behavior in the results portion of the WWDC session.
- On a normal Stop action, stop producing new microphone audio and remove the tap once.
- Finish the
AsyncStreaminput continuation so the analyzer can see the end of input. - Await
finalizeAndFinishThroughEndOfInput(), while the result-consumer task remains alive. - Wait for result consumption to complete, then commit final text and release session resources.
Finalization waits for input termination and consumption. Calling it while the producer never finishes can leave Stop waiting. Cancelling the result task immediately can also lose the tail you intended to save. A Cancel action that discards an utterance should use a deliberate cancellation path instead of pretending it was a normal finalized result.
The public package remains a learning sample. Its current stop() finalizes and then cancels the consumer; it does not expose a separate consumer-completion acknowledgement. If retaining every final result matters, adapt that lifecycle and test it. Also test Stop while preparation is in progress, repeated taps, view dismissal, interruption, and restart after an error.
Vocabulary support depends on the transcriber module
It is inaccurate to say the new Speech API has no contextual vocabulary support. Apple documents AnalysisContext.contextualStrings for DictationTranscriber. The documentation recommends brief phrases and at most 100 phrases across tags. That is not a promise that the same hints customize SpeechTranscriber.
// For an analyzer configured with DictationTranscriber.
let context = AnalysisContext()
context.contextualStrings[.general] = ["Obsidian", "Yurica"]
try await analyzer.setContext(context)
setContext(_:) replaces the current context, so preserve or merge any existing application context when needed. The downloadable helper rejects more than 100 phrases instead of silently truncating them. Phrase hints are distinct from the customized language-model option also documented for DictationTranscriber, and neither guarantees correct recognition of every name. Choose and test the module and language needed by your app.
When recording produces no text, locate the failing stage
| Symptom | Check next | Useful evidence |
|---|---|---|
| Preparation fails | Device support, normalized locale, asset status and reservation/download errors | OS and device, requested/selected locale, error domain and code |
| Mic UI is active, no buffers | Permission, audio-session activation, route, engine start and tap lifecycle | Whether callbacks arrive; input sample rate and channel count |
| Buffers arrive, no text | Compatible format, nonzero converter output, yielded inputs and active results task | Input/output frame counts and the first actual pipeline error |
| Words repeat | Whether volatile revisions are being appended instead of replaced | Result audio ranges and final/volatile flags |
| Last words disappear | Input completion and consumer lifetime during finalization | Last input time, finalization completion and final result receipt |
| Offline launch fails | Required model readiness on that device and locale | Asset status before disconnecting and the surfaced setup error |
For diagnostics, start with stage timings, frame counts, locale, and error information. Raw recordings and recognized text are not needed for every log entry. If a user shares a reproduction, ask for a short nonsensitive test phrase and the relevant configuration rather than a private conversation.
What was checked, and what to measure on a device
This article was checked against Apple documentation and Xcode 26.6 on September 9, 2026. The downloadable helper passes Swift 6 type checking with complete strict concurrency for an iOS 26 Simulator target. It does not record audio, and this check does not demonstrate speech accuracy, successful asset installation, or an end-to-end microphone session.
The public sample at revision 1befcce passed four PCM converter tests and an iOS Simulator build on September 9. Two tests reuse the source buffer after conversion and confirm that mono float and interleaved stereo integer output remain intact; both fail against the previous matching-format passthrough implementation. Its README explicitly says the package refactor has not been re-tested on a physical iOS device. The historical latency numbers previously on this page are not a benchmark for the current package.
For your own app, record device, OS, locale, audio route, model readiness, and the exact timing boundaries. Measure first volatile text, first final text, and normal-stop completion separately. Compare a session with assets ready against a session that requires preparation. Repeat a fixed nonsensitive phrase, and inspect missing or duplicated final words as well as speed. Do not infer Neural Engine behavior or a hardware limit from one timing result.
On Apple Watch, use a separate system text-input flow if it fits your app. TextFieldLink opens the system interface; it does not establish that dictation is already recording. SpeechAnalyzer is unavailable on watchOS, and the iOS package is not a Watch microphone implementation.
Frequently asked questions
Does SpeechAnalyzer work offline?
SpeechTranscriber processes audio on the device, but its required model must be available. Assets can already be present before your app’s first launch. Check and prepare them before an offline session; do not assume either that every first launch must download or that a previously installed model will always remain available.
Can I add custom vocabulary?
Apple documents AnalysisContext.contextualStrings for DictationTranscriber, and that module also exposes a customized-language content hint. This is not documentation of the same feature for SpeechTranscriber. Choose the module deliberately and test the words and locale you need.
Why does the app record but show no text?
Check device and locale support, asset status, microphone input, the optional analyzer format, converter output, the input sequence, and the result consumer. Surface the actual error instead of treating every empty result as a format problem.
How do I keep the last words when stopping?
Stop producing audio, finish the input continuation, and let the analyzer finalize through the end of input. Keep the result consumer alive to receive final results. Cancellation is a separate discard path, not a substitute for normal completion.
Does this sample run on Apple Watch?
No. The linked microphone session is an iOS 26 example, and SpeechAnalyzer is unavailable on watchOS. A Watch app can use system text input such as TextFieldLink; that presents a system input interface and does not prove that dictation recording has begun.
What has been validated for the sample?
The linked repository records four converter tests and an iOS Simulator build on September 9, 2026, including two regressions for reused source-buffer storage. No physical-device retest is claimed. This guide’s preparation helper was type-checked with Xcode 26.6 in Swift 6 complete strict-concurrency mode on September 9. These checks do not establish microphone accuracy, model-download behavior, or latency.
How this relates to Simple Memo
This guide and the free sample are maintained by the SimpleMemo developer. The app’s voice input guide describes the product workflow. The sample itself sends no email and saves nothing to Obsidian. In Simple Memo, email requires recipient setup and verification, and Obsidian saving requires integration and destination settings. Local speech recognition does not describe the privacy or delivery behavior of any later action your app performs.
Related: offline-first memo architecture. The next step for a developer is to run the complete sample with a test phrase and verify preparation, results, and stopping on the device and locale they plan to support.