# Architektur ## Überblick FeelAloud ist eine einzelne native iOS-App ohne Backend und ohne externe Paketabhängigkeiten. Alle Produktdaten werden mit Apple-Frameworks auf dem Gerät verarbeitet. Das Xcode-Projekt enthält derzeit ein App-Target. ## Komponenten | Datei | Verantwortung | | --- | --- | | `FeelAloudApp.swift` | App-Einstieg, SwiftData-Container und globaler Router | | `RootView.swift` | Tabs, Sheets, App-Intent-Ausführung und Follow-up-Auswahl | | `HomeView.swift` | Startseite, Schnellaktionen und 7-Tage-Zusammenfassung | | `EntryFlowView.swift` | Aufnahme-, Analyse-, Prüf- und Speicherablauf | | `SpeechRecognizer.swift` | Mikrofon, lokale deutsche Spracherkennung und Transkript | | `MoodAnalysisService.swift` | Typisierte Extraktion über Foundation Models | | `MoodEntry.swift` | Datenmodell eines ausführlichen Tagebucheintrags | | `MoodSnapshot.swift` | Datenmodell und Regeln eines kurzen Check-ins | | `SnapshotView.swift` | Fünf-Stufen-Auswahl und spätere Kontextfrage | | `SnapshotNotificationScheduler.swift` | Lokale Erinnerungen und Notification-Routing | | `HistoryView.swift` | Zeitraumfilter, Diagramm, Detailansichten und Exportstart | | `SettingsView.swift` | Erinnerungszeiten, KI-Status, CSV-Export und Hinweise | | `XLSXExporter.swift` | OOXML-Arbeitsmappe und minimaler ZIP-Writer | | `CSVExporter.swift` | Semikolon-CSV mit UTF-8-BOM für Numbers/Excel | | `StartDiaryEntryIntent.swift` | Kurzbefehl und Action-Button-Integration | | `ShareSheet.swift` | Übergang zum systemweiten iOS-Share-Sheet | ## Datenfluss: ausführlicher Eintrag 1. `EntryFlowView` startet `SpeechRecognizer`. 2. `SpeechRecognizer` fordert Mikrofon- und Speech-Berechtigung an und setzt `requiresOnDeviceRecognition = true`. 3. Nach dem Stoppen erhält `MoodAnalysisService` ausschließlich das erkannte Transkript. 4. `LanguageModelSession` erzeugt eine typisierte `GeneratedDiaryEntry`-Antwort. 5. Die Person prüft und korrigiert alle Werte im Formular. 6. Erst die bestätigten Werte werden als `MoodEntry` in SwiftData gespeichert. Ist Sprache oder Foundation Models nicht verfügbar, kann die Person zur manuellen Eingabe wechseln. Die App fällt nicht auf einen eigenen Cloud-Dienst zurück. ## Datenfluss: Momentaufnahme 1. Ein Check-in wird manuell oder über eine lokale Mitteilung geöffnet. 2. Ein Tipp erzeugt einen `MoodSnapshot` mit Wert 1 bis 5 und Quelle. 3. Bei **Sehr gut**, **Schlecht** oder **Sehr schlecht** bleibt `awaitsFollowUp` aktiv. 4. Beim nächsten normalen Aktivieren der App am selben Kalendertag sucht `RootView` den jüngsten offenen Snapshot. 5. Eine Antwort setzt `followUpNote`; **Heute nicht mehr fragen** setzt `followUpDismissed`. Das direkte Öffnen aus einer Mitteilung unterdrückt genau eine sofortige Follow-up-Prüfung, damit zuerst die gewünschte Fünf-Stufen-Auswahl erscheint. ## Persistenz Der `ModelContainer` verwaltet zwei SwiftData-Modelle: ### MoodEntry | Feld | Zweck | | --- | --- | | `id`, `createdAt` | Eindeutigkeit und Zeitpunkt | | `moodRaw` | Dreistufige Bewertung des ausführlichen Eintrags | | `musicInHead`, `songName`, `earwormStrengthRaw` | Musik-/Ohrwurmangaben | | `socialContact` | Sozialkontakt vorhanden | | `distressRaw` | Leidensdruck durch Musik im Kopf | | `countermeasures`, `countermeasureActivity` | Gegenmaßnahmen | | `stress` | Stress, wenn keine Musik im Kopf vorhanden ist | | `transcript` | Bestätigter gesprochener Text | ### MoodSnapshot | Feld | Zweck | | --- | --- | | `id`, `createdAt` | Eindeutigkeit und Zeitpunkt | | `ratingRaw` | Fünfstufige Bewertung | | `followUpNote` | Später ergänzter Kontext | | `followUpDismissed` | Kontextfrage für diesen Snapshot verworfen | | `sourceRaw` | `manual` oder `notification` | Für zukünftige Schemaänderungen muss vor Veröffentlichung einer inkompatiblen Version ein `VersionedSchema` mit `SchemaMigrationPlan` eingeführt werden. ## Routing und Lebenszyklus `AppRouter` ist ein `@MainActor`-gebundener, beobachtbarer Singleton. Er hält nur flüchtigen UI-Zustand. `RootView` reagiert auf: - manuelle Aktionen der Oberfläche; - `StartDiaryEntryIntent`; - Antworten auf lokale Mitteilungen; - Wechsel der `scenePhase` zurück zu `.active`. Persistente Produktdaten liegen nicht im Router, sondern ausschließlich in SwiftData beziehungsweise `@AppStorage` für Erinnerungseinstellungen. ## Erinnerungen `SnapshotNotificationScheduler` verwendet drei stabile Request-IDs. Beim Ändern der Konfiguration werden bestehende Requests entfernt und neu geplant. Die Trigger enthalten nur Stunde und Minute und wiederholen sich täglich. Es handelt sich um lokale Notifications. APNs, Push-Zertifikate und ein Server sind nicht erforderlich. ## Export Der XLSX-Exporter erstellt drei Arbeitsblätter: 1. **Übersicht** – Zeitraum, Anzahl, Durchschnitt und Verteilung; 2. **Momentaufnahmen** – Zeitpunkt, Bewertung, Kontext und Quelle; 3. **Tagebuch** – strukturierte Felder und Transkript. Die Arbeitsmappe besteht aus OOXML-Dateien in einem unkomprimierten ZIP-Archiv. Text wird XML-escaped; ZIP-Einträge erhalten CRC32-Prüfsummen. Der CSV-Export bleibt als kompatibler Alt-Export für ausführliche Einträge erhalten. Temporäre Exportdateien werden im temporären App-Verzeichnis erzeugt und erst nach einer ausdrücklichen Aktion an das Share-Sheet übergeben. ## Fehler- und Verfügbarkeitsmodell - Fehlende Berechtigungen werden als verständliche, lokale Fehlermeldung angezeigt. - Fehlende lokale Spracherkennung verhindert eine versehentliche Cloud- Transkription. - Vor jeder KI-Nutzung wird `SystemLanguageModel.default.availability` geprüft. - Fehler beim Speichern werden aktuell nicht sichtbar eskaliert (`try?`). Vor einem öffentlichen Release sollte dafür ein zentraler Fehlerpfad ergänzt werden. - FeelAloud erteilt keine medizinischen Ratschläge und gibt dem Modell ausdrücklich nur eine Extraktionsaufgabe. ## Abhängigkeiten Es gibt keine Drittanbieterpakete. Verwendete Frameworks: - AppIntents - AVFoundation - Charts - Foundation / FoundationModels - Observation - Speech - SwiftData - SwiftUI - UIKit - UserNotifications