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>
60 lines
1.7 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|