- 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
12 KiB
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
// 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
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
// 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
updatedTimestamp 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
// 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
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
// 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)
- ✅ 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
- Entscheidung: Welche Stufe soll zuerst implementiert werden?
- Prototyping: Quick PoC für CoreData Schema und Cache-Logic
- UI/UX Design: Mockups für Offline-Indikatoren und Settings
- Implementation: Schrittweise nach Roadmap
- Testing: Ausgiebiges Testen von Edge Cases
- Beta: TestFlight mit fokussiertem Offline-Testing
- Launch: Schrittweises Rollout mit Feature-Flags
Dokument erstellt: 2025-11-01 Version: 1.0