Skip to content

Instantly share code, notes, and snippets.

@lanserxt
Last active October 26, 2025 15:13
Show Gist options
  • Save lanserxt/77780783adfa179c7a009042b9cf5a95 to your computer and use it in GitHub Desktop.
Save lanserxt/77780783adfa179c7a009042b9cf5a95 to your computer and use it in GitHub Desktop.
iOS 26: SpeechRecognizer example
//
// SpeechTranscribeView.swift
// WWDC25Demo
//
// Created by Anton Gubarenko on 20.07.2025.
//
import SwiftUI
import Playgrounds
import Speech
/// Demo View for SpeechRecognizer
struct SpeechTranscribeView: View {
@State private var transcribedText: AttributedString = ""
var body: some View {
VStack {
Text("Transcribe speech")
ScrollView(.vertical, content: {
Text(transcribedText)
.frame(maxHeight: .infinity)
.multilineTextAlignment(.leading)
})
Spacer()
}.task{
do {
try await transcribeFile()
} catch {
print(error)
}
}
}
/// Main recognition task
private func transcribeFile() async throws {
var finalStr: AttributedString = ""
let fileUrl = try loadAudioFile(named: "dragon_sample", withExtension: "mp3")
let locale = Locale(identifier: "it_IT")
let transcriber = transcriber(for: locale)
print("transcriber ready")
try await ensureModel(transcriber: transcriber, locale: locale)
print("ensureModel ready")
let analyzer = SpeechAnalyzer(modules: [transcriber])
if let lastSample = try await analyzer.analyzeSequence(from: AVAudioFile(forReading: fileUrl)) {
try await analyzer.finalizeAndFinish(through: lastSample)
} else {
await analyzer.cancelAndFinishNow()
}
for try await recResponse in transcriber.results {
if recResponse.isFinal {
finalStr.append(recResponse.text)
}
}
transcribedText = finalStr
}
/// Loading audio file
/// - Parameters:
/// - name: file name
/// - ext: extension
/// - Returns: URL of file
private func loadAudioFile(named name: String, withExtension ext: String) throws -> URL {
guard let url = Bundle.main.url(forResource: name, withExtension: ext) else {
throw NSError(domain: "SpeechAnalyzerExample", code: 1, userInfo: [NSLocalizedDescriptionKey: "Audio file not found in bundle."])
}
return url
}
/// Transcriber for locale
/// - Parameter locale: locale to recognize
/// - Returns: Transcriber
private func transcriber(for locale: Locale) -> SpeechTranscriber {
SpeechTranscriber(locale: locale,
transcriptionOptions: [],
reportingOptions: [.volatileResults],
attributeOptions: [.audioTimeRange])
}
/// Chekc that model exists
/// - Parameters:
/// - transcriber: transcriber to use for this model
/// - locale: locale to recognize
private func ensureModel(transcriber: SpeechTranscriber, locale: Locale) async throws {
guard await supported(locale: locale) else {
throw NSError(domain: "SpeechAnalyzerExample", code: 1, userInfo: [NSLocalizedDescriptionKey: "Locale not supported"])
}
if await installed(locale: locale) {
return
} else {
try await downloadIfNeeded(for: transcriber)
}
}
/// Is locale supported
/// - Parameter locale: locale to recognize
/// - Returns: true if supported, fasle - else
private func supported(locale: Locale) async -> Bool {
print(SpeechTranscriber.isAvailable)
guard SpeechTranscriber.isAvailable else {return false}
let supported = await DictationTranscriber.supportedLocales
print("Have locales \(supported)")
return supported.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47))
}
/// Is locale file installed locally
/// - Parameter locale: locale to recognize
/// - Returns: true is installed locally
private func installed(locale: Locale) async -> Bool {
let installed = await Set(SpeechTranscriber.installedLocales)
return installed.map { $0.identifier(.bcp47) }.contains(locale.identifier(.bcp47))
}
/// Download module assets
/// - Parameter module: module to use
private func downloadIfNeeded(for module: SpeechTranscriber) async throws {
if let downloader = try await AssetInventory.assetInstallationRequest(supporting: [module]) {
try await downloader.downloadAndInstall()
}
}
}
#Preview {
SpeechTranscribeView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment