Files
voicediary/VoiceDiary/ViewModels/RecordingViewModel.swift
Felix Förtsch b442c13719 fix CloudKit compatibility: default values, optional relationship, background mode
- Add default values to all SwiftData attributes (CloudKit requirement)
- Make memos relationship optional (CloudKit requirement)
- Add allMemos computed property for safe unwrapping
- Add remote-notification background mode for CloudKit push sync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:11:40 +01:00

94 lines
2.2 KiB
Swift

import AVFoundation
import Foundation
import SwiftData
@Observable
@MainActor
final class RecordingViewModel {
var isRecording = false
var recordingDuration: TimeInterval = 0
var error: Error?
private let recorder = AudioRecorderService()
private let transcriptionService = TranscriptionService()
private var currentRecordingURL: URL?
var formattedDuration: String {
let minutes = Int(recorder.recordingDuration) / 60
let seconds = Int(recorder.recordingDuration) % 60
return String(format: "%d:%02d", minutes, seconds)
}
func startRecording() {
do {
currentRecordingURL = try recorder.startRecording()
isRecording = true
error = nil
} catch {
self.error = error
}
}
func stopRecording(context: ModelContext) {
guard let result = recorder.stopRecording() else { return }
isRecording = false
let today = Calendar.current.startOfDay(for: .now)
let entry = fetchOrCreateEntry(for: today, context: context)
let memo = VoiceMemo(
audioFileName: result.url.lastPathComponent,
duration: result.duration
)
context.insert(memo)
memo.entry = entry
if entry.memos == nil { entry.memos = [] }
entry.memos?.append(memo)
entry.updatedAt = .now
try? context.save()
let audioURL = memo.audioURL
Task {
await transcribeMemo(memo, audioURL: audioURL)
}
}
private func fetchOrCreateEntry(for date: Date, context: ModelContext) -> DiaryEntry {
let descriptor = FetchDescriptor<DiaryEntry>()
if let entries = try? context.fetch(descriptor) {
let match = entries.first { entry in
Calendar.current.isDate(entry.date, inSameDayAs: date)
}
if let match { return match }
}
let entry = DiaryEntry(date: date)
context.insert(entry)
return entry
}
private func transcribeMemo(_ memo: VoiceMemo, audioURL: URL) async {
if transcriptionService.authorizationStatus == .notDetermined {
await transcriptionService.requestAuthorization()
}
guard transcriptionService.authorizationStatus == .authorized else {
return
}
memo.isTranscribing = true
do {
let transcript = try await transcriptionService.transcribe(audioURL: audioURL)
memo.transcript = transcript
memo.isTranscribing = false
memo.entry?.updatedAt = .now
} catch {
memo.isTranscribing = false
self.error = error
}
}
}