ReadKeep/documentation/Offline-Konzept.md
Ilyas Hallak e4121aa066 Add implementation tracking and planning documents
- Offline-Konzept.md: Multi-level offline strategy (3 stages)
- Offline-Stufe1-Implementierung.md: Detailed implementation plan for stage 1
- Offline-Stufe1-Implementation-Tracking.md: Day-by-day tracking with checkboxes

These documents will guide the offline sync implementation over multiple days
2025-11-08 23:15:17 +01:00

379 lines
12 KiB
Markdown
Raw 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.

# Offline-Konzept für Readeck iOS App
## Übersicht
Dieses Dokument beschreibt ein mehrstufiges Konzept zur Offline-Funktionalität der Readeck iOS App. Das Konzept ist modular aufgebaut und ermöglicht eine schrittweise Implementierung von einer einfachen Caching-Strategie für ungelesene Artikel bis hin zu einer vollständig offline-fähigen App.
---
## Stufe 1: Smart Cache für Unread Items (Basis-Offline)
### Beschreibung
Die App lädt automatisch eine konfigurierbare Anzahl ungelesener Artikel herunter und hält diese offline verfügbar. Dies ist die Grundlage für eine bessere Offline-Erfahrung ohne großen Implementierungsaufwand.
### Features
- **Automatisches Caching**: Beim App-Start und bei Pull-to-Refresh werden die neuesten ungelesenen Artikel im Hintergrund heruntergeladen
- **Konfigurierbare Anzahl**: User kann in den Einstellungen festlegen, wie viele Artikel gecacht werden sollen (z.B. 10, 25, 50, 100)
- **Nur Artikel-Content**: Es wird nur der HTML-Content des Artikels (`/api/bookmarks/{id}/article`) gecached
- **Automatische Verwaltung**: Ältere gecachte Artikel werden automatisch entfernt, wenn neue hinzukommen (FIFO-Prinzip)
- **Offline-Indikator**: In der Bookmark-Liste wird angezeigt, welche Artikel offline verfügbar sind
### Technische Umsetzung
```swift
// Neue Settings
struct OfflineSettings {
var enabled: Bool = true
var maxUnreadArticles: Int = 25 // 10, 25, 50, 100
var onlyWiFi: Bool = true
}
// Neue Repository-Methode
protocol PBookmarksRepository {
func cacheBookmarkArticle(id: String, html: String) async throws
func getCachedArticle(id: String) -> String?
func hasCachedArticle(id: String) -> Bool
}
```
### Datenspeicherung
- **CoreData** für Artikel-HTML und Metadaten (Titel, URL, Datum, Reihenfolge)
- **FileManager** optional für große HTML-Dateien (falls CoreData zu groß wird)
### User Experience
- Bookmark-Liste zeigt Download-Icon für offline verfügbare Artikel
- Beim Öffnen eines gecachten Artikels: Sofortiges Laden ohne Netzwerk-Anfrage
- In Settings: "Offline-Modus" Sektion mit Slider für Anzahl der Artikel
- Cache-Größe wird angezeigt (z.B. "23 Artikel, 12.5 MB")
---
## Stufe 2: Offline-First mit Sync (Erweitert)
### Beschreibung
Die App funktioniert vollständig offline. Alle Aktionen werden lokal gespeichert und bei Netzwerkverbindung mit dem Server synchronisiert.
### Features
- **Vollständige Offline-Funktionalität**: Alle Lese-Operationen funktionieren offline
- **Lokale Schreib-Operationen**:
- Bookmarks erstellen, bearbeiten, löschen
- Labels hinzufügen/entfernen
- Artikel archivieren/favorisieren
- Lesefortschritt speichern
- Annotationen/Highlights erstellen
- **Intelligente Synchronisierung**:
- Automatische Sync bei Netzwerkverbindung
- Konfliktauflösung (Server gewinnt vs. Client gewinnt)
- Retry-Mechanismus bei fehlgeschlagenen Syncs
- **Sync-Status**: User sieht jederzeit, ob und was synchronisiert wird
- **Offline-Indikator**: Klarer Status in der UI (Online/Offline/Syncing)
### Technische Umsetzung
#### Sync-Manager
```swift
class OfflineSyncManager {
// Bereits vorhanden für Bookmark-Erstellung
// Erweitern für alle Operations
enum SyncOperation {
case createBookmark(CreateBookmarkRequest)
case updateBookmark(id: String, BookmarkUpdateRequest)
case deleteBookmark(id: String)
case addLabels(bookmarkId: String, labels: [String])
case removeLabels(bookmarkId: String, labels: [String])
case updateReadProgress(bookmarkId: String, progress: Int)
case createAnnotation(AnnotationRequest)
case deleteAnnotation(bookmarkId: String, annotationId: String)
}
func queueOperation(_ operation: SyncOperation) async
func syncAllPendingOperations() async throws
func getPendingOperationsCount() -> Int
}
```
#### Lokale Datenbank-Struktur
```swift
// CoreData Entities
entity OfflineBookmark {
id: String
title: String
url: String
content: String? // HTML
metadata: Data // JSON mit allen Bookmark-Daten
labels: [String]
isArchived: Bool
isMarked: Bool
readProgress: Int
annotations: [OfflineAnnotation]
lastModified: Date
syncStatus: String // "synced", "pending", "conflict"
}
entity PendingSyncOperation {
id: UUID
type: String // operation type
payload: Data // JSON der Operation
createdAt: Date
retryCount: Int
lastError: String?
}
```
### Sync-Strategien
#### Option A: Last-Write-Wins (Einfach)
- Server-Version überschreibt bei Konflikt immer lokale Version
- Einfach zu implementieren
- Potentieller Datenverlust bei Offline-Änderungen
#### Option B: Timestamp-basiert (Empfohlen)
- Neueste Änderung (basierend auf Timestamp) gewinnt
- Server sendet `updated` Timestamp mit jeder Response
- Client vergleicht mit lokalem Timestamp
#### Option C: Operational Transformation (Komplex)
- Granulare Merge-Strategien für verschiedene Felder
- Beispiel: Lokale Labels + Server Labels = Union
- Aufwändig, aber maximale Datenerhaltung
### User Experience
- **Offline-Banner**: "Du bist offline. Änderungen werden synchronisiert, sobald du online bist."
- **Sync-Status Indicator**:
- Grün: Alles synchronisiert
- Gelb: Synchronisierung läuft
- Rot: Sync-Fehler
- Grau: Offline
- **Pending Changes Badge**: Zeigt Anzahl nicht synchronisierter Änderungen
- **Manual Sync Button**: Manuelles Anstoßen der Synchronisierung
- **Sync-Konflikt-Dialog**: Bei Konflikten User entscheiden lassen (Lokal behalten / Server übernehmen / Beide behalten)
---
## Stufe 3: Vollständig Offline-Fähig (Maximum)
### Beschreibung
Die App kann komplett ohne Server-Verbindung genutzt werden, inklusive lokaler Volltext-Suche und erweiterter Offline-Features.
### Zusätzliche Features
- **Lokale Volltext-Suche**:
- SQLite FTS5 (Full-Text Search) für schnelle Suche
- Suche in Titeln, URLs, Content, Labels
- Highlighting von Suchbegriffen
- **Intelligente Offline-Strategie**:
- Predictive Caching basierend auf Leseverhalten
- Automatisches Herunterladen von "Ähnlichen Artikeln"
- Background Refresh für häufig gelesene Labels/Tags
- **Erweiterte Export-Funktionen**:
- Kompletten Offline-Cache als ZIP exportieren
- Import von Offline-Daten auf anderem Gerät
- Backup & Restore
- **Reader Mode Optimierungen**:
- Lokale Schriftarten für Offline-Nutzung
- CSS/JS lokal gespeichert
- Keine externen Dependencies
- **Offline-Statistiken**:
- Lesezeit offline vs. online
- Meistgelesene offline Artikel
- Speicherplatz-Statistiken
### Erweiterte Technische Umsetzung
#### FTS5 für Suche
```swift
// SQLite Schema
CREATE VIRTUAL TABLE bookmarks_fts USING fts5(
title,
url,
content,
labels,
content='offline_bookmarks',
content_rowid='id'
);
// Suche
func searchOfflineBookmarks(query: String) -> [Bookmark] {
let sql = """
SELECT * FROM offline_bookmarks
WHERE id IN (
SELECT rowid FROM bookmarks_fts
WHERE bookmarks_fts MATCH ?
)
ORDER BY rank
"""
// Execute and return results
}
```
#### Predictive Caching
```swift
class PredictiveCacheManager {
// Analysiere Leseverhalten
func analyzeReadingPatterns() -> ReadingProfile
// Lade ähnliche Artikel basierend auf:
// - Gleiche Labels/Tags
// - Gleiche Autoren
// - Gleiche Domains
func prefetchRelatedArticles(for bookmark: Bookmark) async
// Machine Learning (optional)
// CoreML Model für Content-Empfehlungen
func trainRecommendationModel()
}
```
#### Background Sync
```swift
// BackgroundTasks Framework
class BackgroundSyncScheduler {
func scheduleBackgroundSync() {
let request = BGAppRefreshTaskRequest(identifier: "de.readeck.sync")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 min
try? BGTaskScheduler.shared.submit(request)
}
func handleBackgroundSync(task: BGTask) async {
// Sync neue Artikel
// Update gecachte Artikel
// Cleanup alte Artikel
}
}
```
### Datenspeicherung
- **SQLite + FTS5**: Für Volltextsuche
- **CoreData**: Für strukturierte Daten und Relationships
- **FileManager**: Für HTML-Content, Bilder, Assets
- **NSUbiquitousKeyValueStore**: Optional für iCloud-Sync der Einstellungen
### User Experience
- **Offline-First Ansatz**: App fühlt sich immer schnell an, auch bei schlechter Verbindung
- **Smart Downloads**:
- "20 neue Artikel verfügbar. Jetzt herunterladen?"
- "Für dich empfohlen: 5 Artikel zum Offline-Lesen"
- **Storage Dashboard**:
- Visualisierung des genutzten Speichers
- Top 10 größte Artikel
- "Speicher optimieren" Funktion (Bilder komprimieren, alte Artikel löschen)
- **Offline-Modus Toggle**: Bewusster "Nur Offline"-Modus aktivierbar
- **Sync-Schedule**: "Täglich um 6:00 Uhr synchronisieren"
---
## Implementierungs-Roadmap
### Phase 1: Basis (Stufe 1) - ca. 2-3 Wochen
- [ ] Settings für Offline-Anzahl
- [ ] CoreData Schema für cached Articles
- [ ] Download-Logic in BookmarksRepository
- [ ] UI-Indikatoren für gecachte Artikel
- [ ] Cleanup-Logic (FIFO)
### Phase 2: Offline-First mit Sync (Stufe 2) - ca. 4-6 Wochen
- [ ] Erweiterte CoreData Entities
- [ ] OfflineSyncManager erweitern
- [ ] Konfliktauflösung implementieren
- [ ] Sync-Status UI
- [ ] Comprehensive Testing (Edge Cases)
### Phase 3: Advanced Features (Stufe 3) - ca. 4-6 Wochen
- [ ] SQLite FTS5 Integration
- [ ] Predictive Caching
- [ ] Background Tasks
- [ ] Export/Import
- [ ] Analytics & Optimizations
---
## Technische Überlegungen
### Performance
- **Lazy Loading**: Artikel-Content nur laden, wenn benötigt
- **Pagination**: Auch offline große Listen paginieren
- **Image Optimization**: Bilder komprimieren vor dem Speichern (WebP, HEIC)
- **Incremental Sync**: Nur Änderungen synchronisieren, nicht alles neu laden
### Speicherplatz
- **Quotas**: Maximale Größe für Offline-Cache (z.B. 500MB, 1GB, 2GB)
- **Cleanup-Strategien**:
- Älteste zuerst (FIFO)
- Größte zuerst
- Am wenigsten gelesen zuerst
- **Kompression**: HTML und JSON komprimieren (gzip)
### Sicherheit
- **Verschlüsselung**: Offline-Daten mit iOS Data Protection verschlüsseln
- **Sensitive Data**: Passwörter niemals lokal speichern (nur Token mit Keychain)
- **Cleanup bei Logout**: Alle Offline-Daten löschen
### Testing
- **Unit Tests**: Für Sync-Logic, Conflict Resolution
- **Integration Tests**: Offline→Online Szenarien
- **UI Tests**: Offline-Modi, Sync-Status
- **Edge Cases**:
- App-Kill während Sync
- Netzwerk-Loss während Download
- Speicher voll
- Server-Konflikte
---
## Bestehende Features die von Offline profitieren
### Bereits implementiert
-**Offline Bookmark Erstellung**: Bookmarks werden lokal gespeichert und bei Verbindung synchronisiert ([OfflineSyncManager.swift](readeck/Data/Repository/OfflineSyncManager.swift))
-**Server Reachability Check**: Check ob Server erreichbar ist vor Sync-Operationen
-**Sync Status UI**: isSyncing und syncStatus werden bereits getrackt
### Erweiterbar
- **Text-to-Speech**: Geht bereits offline, wenn Artikel gecacht ist
- **Annotations/Highlights**: Können offline erstellt und später synchronisiert werden
- **Lesefortschritt**: Kann lokal getrackt und bei Sync übertragen werden
- **Labels**: Offline hinzufügen/entfernen mit Sync
- **Read Progress**: Bereits vorhanden im BookmarkDetail Model, kann offline getrackt werden
---
## Metriken für Erfolg
### User-Metriken
- Durchschnittliche Offline-Nutzungszeit
- % der Nutzer, die Offline-Features aktivieren
- Anzahl der offline gelesenen Artikel pro User
- User-Feedback zur Offline-Erfahrung
### Technische Metriken
- Erfolgsrate der Synchronisierungen
- Durchschnittliche Sync-Dauer
- Anzahl der Sync-Konflikte
- Speicherplatz-Nutzung pro User
- Crash-Rate während Offline-Operationen
### Performance-Metriken
- App-Start-Zeit mit/ohne Offline-Cache
- Ladezeit für gecachte vs. nicht-gecachte Artikel
- Netzwerk-Traffic Reduktion durch Caching
- Battery Impact durch Background-Sync
---
## Nächste Schritte
1. **Entscheidung**: Welche Stufe soll zuerst implementiert werden?
2. **Prototyping**: Quick PoC für CoreData Schema und Cache-Logic
3. **UI/UX Design**: Mockups für Offline-Indikatoren und Settings
4. **Implementation**: Schrittweise nach Roadmap
5. **Testing**: Ausgiebiges Testen von Edge Cases
6. **Beta**: TestFlight mit fokussiertem Offline-Testing
7. **Launch**: Schrittweises Rollout mit Feature-Flags
---
*Dokument erstellt: 2025-11-01*
*Version: 1.0*