Initial FeelAloud project
This commit is contained in:
152
docs/ARCHITECTURE.md
Normal file
152
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user