Files
FeelAloud/docs/ARCHITECTURE.md
2026-07-22 22:18:33 +02:00

153 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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