153 lines
6.0 KiB
Markdown
153 lines
6.0 KiB
Markdown
# 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
|