Files
voicediary/VoiceDiary/Services/TranscriptionService.swift
Felix Förtsch dca03214b0 initial VoiceDiary iOS app setup
SwiftUI + SwiftData + iCloud, Apple Speech transcription (German),
audio recording, summarization service protocol (LLM-ready),
localization scaffolding (EN/DE/ES/FR), basic tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:57:41 +01:00

60 lines
1.7 KiB
Swift

import Foundation
import Speech
@Observable
final class TranscriptionService {
var authorizationStatus: SFSpeechRecognizerAuthorizationStatus = .notDetermined
private let speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "de-DE"))
func requestAuthorization() async {
authorizationStatus = await withCheckedContinuation { continuation in
SFSpeechRecognizer.requestAuthorization { status in
continuation.resume(returning: status)
}
}
}
func transcribe(audioURL: URL) async throws -> String {
guard let speechRecognizer, speechRecognizer.isAvailable else {
throw TranscriptionError.recognizerUnavailable
}
let request = SFSpeechURLRecognitionRequest(url: audioURL)
request.requiresOnDeviceRecognition = true
request.shouldReportPartialResults = false
request.addsPunctuation = true
let result = try await speechRecognizer.recognitionTask(with: request)
return result.bestTranscription.formattedString
}
}
enum TranscriptionError: LocalizedError {
case recognizerUnavailable
case noResult
var errorDescription: String? {
switch self {
case .recognizerUnavailable:
String(localized: "transcription.error.unavailable")
case .noResult:
String(localized: "transcription.error.noResult")
}
}
}
private extension SFSpeechRecognizer {
func recognitionTask(with request: SFSpeechRecognitionRequest) async throws -> SFSpeechRecognitionResult {
try await withCheckedThrowingContinuation { continuation in
recognitionTask(with: request) { result, error in
if let error {
continuation.resume(throwing: error)
} else if let result, result.isFinal {
continuation.resume(returning: result)
}
}
}
}
}