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
This commit is contained in:
Ilyas Hallak 2025-11-01 21:19:00 +01:00
parent db0ce09b4c
commit e4121aa066
7 changed files with 2848 additions and 0 deletions

BIN
documentation/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,378 @@
# 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*

View File

@ -0,0 +1,670 @@
# Offline Stufe 1 - Implementierungs-Tracking
**Branch**: `offline-sync`
**Start**: 2025-11-01
**Geschätzte Dauer**: 5-8 Tage
---
## Phase 1: Foundation & Models (Tag 1)
### 1.1 Logging-Kategorie erweitern
**Datei**: `readeck/Utils/Logger.swift`
- [ ] `LogCategory.sync` zur enum hinzufügen
- [ ] `Logger.sync` zur Extension hinzufügen
- [ ] Testen: Logging funktioniert
**Code-Änderungen**:
```swift
enum LogCategory: String, CaseIterable, Codable {
// ... existing
case sync = "Sync"
}
extension Logger {
// ... existing
static let sync = Logger(category: .sync)
}
```
---
### 1.2 Domain Model: OfflineSettings
**Neue Datei**: `readeck/Domain/Model/OfflineSettings.swift`
- [ ] Struct OfflineSettings erstellen
- [ ] Properties hinzufügen:
- [ ] `enabled: Bool = true`
- [ ] `maxUnreadArticles: Double = 20`
- [ ] `saveImages: Bool = false`
- [ ] `lastSyncDate: Date?`
- [ ] Computed Property `maxUnreadArticlesInt` implementieren
- [ ] Computed Property `shouldSyncOnAppStart` implementieren (4-Stunden-Check)
- [ ] Codable Conformance
- [ ] Testen: shouldSyncOnAppStart Logic
**Checklist**:
- [ ] File erstellt
- [ ] Alle Properties vorhanden
- [ ] 4-Stunden-Check funktioniert
- [ ] Kompiliert ohne Fehler
---
### 1.3 CoreData Entity: CachedArticle
**Datei**: `readeck.xcdatamodeld`
- [ ] Neue Model-Version erstellen
- [ ] CachedArticle Entity hinzufügen
- [ ] Attributes definieren:
- [ ] `id` (String, indexed)
- [ ] `bookmarkId` (String, indexed, unique constraint)
- [ ] `bookmarkJSON` (String)
- [ ] `htmlContent` (String)
- [ ] `cachedDate` (Date, indexed)
- [ ] `lastAccessDate` (Date)
- [ ] `size` (Integer 64)
- [ ] `imageURLs` (String, optional)
- [ ] Current Model Version setzen
- [ ] Lightweight Migration in CoreDataManager aktivieren
- [ ] Testen: App startet ohne Crash, keine Migration-Fehler
**Checklist**:
- [ ] Entity erstellt
- [ ] Alle Attributes vorhanden
- [ ] Indexes gesetzt
- [ ] Migration funktioniert
- [ ] App startet erfolgreich
---
## Phase 2: Data Layer (Tag 1-2)
### 2.1 Settings Repository Protocol
**Neue Datei**: `readeck/Domain/Protocols/PSettingsRepository.swift`
- [ ] Protocol `PSettingsRepository` erstellen
- [ ] Methode `loadOfflineSettings()` definieren
- [ ] Methode `saveOfflineSettings(_ settings: OfflineSettings)` definieren
**Checklist**:
- [ ] File erstellt
- [ ] Protocol definiert
- [ ] Methoden deklariert
- [ ] Kompiliert ohne Fehler
---
### 2.2 Settings Repository Implementation
**Neue Datei**: `readeck/Data/Repository/SettingsRepository.swift`
- [ ] Class `SettingsRepository` erstellen
- [ ] `PSettingsRepository` implementieren
- [ ] `loadOfflineSettings()` implementieren:
- [ ] UserDefaults laden
- [ ] JSON dekodieren
- [ ] Default-Settings zurückgeben bei Fehler
- [ ] Logger.data für Erfolgsmeldung
- [ ] `saveOfflineSettings()` implementieren:
- [ ] JSON enkodieren
- [ ] UserDefaults speichern
- [ ] Logger.data für Erfolgsmeldung
- [ ] Testen: Save & Load funktioniert
**Checklist**:
- [ ] File erstellt
- [ ] loadOfflineSettings() implementiert
- [ ] saveOfflineSettings() implementiert
- [ ] Logging integriert
- [ ] Manueller Test erfolgreich
---
### 2.3 BookmarksRepository Protocol erweitern
**Datei**: `readeck/Domain/Protocols/PBookmarksRepository.swift`
- [ ] Neue Methoden zum Protocol hinzufügen:
- [ ] `cacheBookmarkWithMetadata(bookmark:html:saveImages:) async throws`
- [ ] `getCachedArticle(id:) -> String?`
- [ ] `hasCachedArticle(id:) -> Bool`
- [ ] `getCachedBookmarks() async throws -> [Bookmark]`
- [ ] `getCachedArticlesCount() -> Int`
- [ ] `getCacheSize() -> String`
- [ ] `clearCache() async throws`
- [ ] `cleanupOldestCachedArticles(keepCount:) async throws`
**Checklist**:
- [ ] Alle Methoden deklariert
- [ ] Async/throws korrekt gesetzt
- [ ] Kompiliert ohne Fehler
---
### 2.4 BookmarksRepository Implementation
**Datei**: `readeck/Data/Repository/BookmarksRepository.swift`
- [ ] `import Kingfisher` hinzufügen
- [ ] `cacheBookmarkWithMetadata()` implementieren:
- [ ] Prüfen ob bereits gecacht
- [ ] Bookmark als JSON enkodieren
- [ ] CoreData CachedArticle erstellen
- [ ] Speichern in CoreData
- [ ] Logger.sync.info
- [ ] Bei saveImages: extractImageURLsFromHTML() aufrufen
- [ ] Bei saveImages: prefetchImagesWithKingfisher() aufrufen
- [ ] `extractImageURLsFromHTML()` implementieren:
- [ ] Regex für `<img src="...">` Tags
- [ ] URLs extrahieren
- [ ] Nur absolute URLs (http/https)
- [ ] Logger.sync.debug
- [ ] `prefetchImagesWithKingfisher()` implementieren:
- [ ] URLs zu URL-Array konvertieren
- [ ] ImagePrefetcher erstellen
- [ ] Optional: downloadPriority(.low) setzen
- [ ] prefetcher.start()
- [ ] Logger.sync.info
- [ ] `getCachedArticle()` implementieren:
- [ ] NSFetchRequest mit predicate
- [ ] lastAccessDate updaten
- [ ] htmlContent zurückgeben
- [ ] `hasCachedArticle()` implementieren
- [ ] `getCachedBookmarks()` implementieren:
- [ ] Fetch alle CachedArticle
- [ ] Sort by cachedDate descending
- [ ] JSON zu Bookmark dekodieren
- [ ] Array zurückgeben
- [ ] `getCachedArticlesCount()` implementieren
- [ ] `getCacheSize()` implementieren:
- [ ] Alle sizes summieren
- [ ] ByteCountFormatter nutzen
- [ ] `clearCache()` implementieren:
- [ ] NSBatchDeleteRequest
- [ ] Logger.sync.info
- [ ] `cleanupOldestCachedArticles()` implementieren:
- [ ] Sort by cachedDate ascending
- [ ] Älteste löschen wenn > keepCount
- [ ] Logger.sync.info
- [ ] Testen: Alle Methoden funktionieren
**Checklist**:
- [ ] Kingfisher import
- [ ] cacheBookmarkWithMetadata() fertig
- [ ] extractImageURLsFromHTML() fertig
- [ ] prefetchImagesWithKingfisher() fertig
- [ ] getCachedArticle() fertig
- [ ] hasCachedArticle() fertig
- [ ] getCachedBookmarks() fertig
- [ ] getCachedArticlesCount() fertig
- [ ] getCacheSize() fertig
- [ ] clearCache() fertig
- [ ] cleanupOldestCachedArticles() fertig
- [ ] Logging überall integriert
- [ ] Manuelle Tests erfolgreich
---
## Phase 3: Use Case & Business Logic (Tag 2)
### 3.1 OfflineCacheSyncUseCase Protocol
**Neue Datei**: `readeck/Domain/UseCase/OfflineCacheSyncUseCase.swift`
- [ ] Protocol `POfflineCacheSyncUseCase` erstellen
- [ ] Published Properties definieren:
- [ ] `var isSyncing: AnyPublisher<Bool, Never>`
- [ ] `var syncProgress: AnyPublisher<String?, Never>`
- [ ] Methoden deklarieren:
- [ ] `func syncOfflineArticles(settings:) async`
- [ ] `func getCachedArticlesCount() -> Int`
- [ ] `func getCacheSize() -> String`
**Checklist**:
- [ ] File erstellt
- [ ] Protocol definiert
- [ ] Methoden deklariert
- [ ] Kompiliert ohne Fehler
---
### 3.2 OfflineCacheSyncUseCase Implementation
**Datei**: `readeck/Domain/UseCase/OfflineCacheSyncUseCase.swift` (im selben File)
- [ ] Class `OfflineCacheSyncUseCase` erstellen
- [ ] Dependencies:
- [ ] `bookmarksRepository: PBookmarksRepository`
- [ ] `settingsRepository: PSettingsRepository`
- [ ] @Published Properties:
- [ ] `_isSyncing = false`
- [ ] `_syncProgress: String?`
- [ ] Publishers als computed properties
- [ ] `syncOfflineArticles()` implementieren:
- [ ] Guard enabled check
- [ ] Logger.sync.info("Starting sync")
- [ ] Set isSyncing = true
- [ ] Fetch bookmarks (state: .unread, limit: settings.maxUnreadArticlesInt)
- [ ] Logger.sync.info("Fetched X bookmarks")
- [ ] Loop durch Bookmarks:
- [ ] Skip wenn bereits gecacht (Logger.sync.debug)
- [ ] syncProgress updaten: "Artikel X/Y..."
- [ ] fetchBookmarkArticle() aufrufen
- [ ] cacheBookmarkWithMetadata() aufrufen
- [ ] successCount++
- [ ] Bei saveImages: syncProgress "...+ Bilder"
- [ ] Catch: errorCount++, Logger.sync.error
- [ ] cleanupOldestCachedArticles() aufrufen
- [ ] lastSyncDate updaten
- [ ] Logger.sync.info("✅ Synced X, skipped Y, failed Z")
- [ ] Set isSyncing = false
- [ ] syncProgress = Status-Message
- [ ] Sleep 3s, dann syncProgress = nil
- [ ] `getCachedArticlesCount()` implementieren
- [ ] `getCacheSize()` implementieren
- [ ] Error-Handling:
- [ ] Catch block für Haupt-Try
- [ ] Logger.sync.error
- [ ] syncProgress = Error-Message
- [ ] Testen: Sync-Flow komplett durchlaufen
**Checklist**:
- [ ] Class erstellt
- [ ] Dependencies injiziert
- [ ] syncOfflineArticles() komplett
- [ ] Success/Skip/Error Tracking
- [ ] Logging an allen wichtigen Stellen
- [ ] Progress-Updates
- [ ] Error-Handling
- [ ] getCachedArticlesCount() fertig
- [ ] getCacheSize() fertig
- [ ] Test: Sync läuft durch
---
## Phase 4: Settings UI (Tag 3)
### 4.1 OfflineSettingsViewModel
**Neue Datei**: `readeck/UI/Settings/OfflineSettingsViewModel.swift`
- [ ] Class mit @MainActor und @Observable
- [ ] @Published Properties:
- [ ] `offlineSettings: OfflineSettings`
- [ ] `isSyncing = false`
- [ ] `syncProgress: String?`
- [ ] `cachedArticlesCount = 0`
- [ ] `cacheSize = "0 KB"`
- [ ] Dependencies:
- [ ] `settingsRepository: PSettingsRepository`
- [ ] `offlineCacheSyncUseCase: POfflineCacheSyncUseCase`
- [ ] Init mit Dependencies
- [ ] `setupBindings()` implementieren:
- [ ] isSyncing Publisher binden
- [ ] syncProgress Publisher binden
- [ ] Auto-save bei offlineSettings change (debounce 0.5s)
- [ ] `loadSettings()` implementieren
- [ ] `syncNow()` implementieren:
- [ ] Kommentar: Manual sync mit höherer Priority
- [ ] await offlineCacheSyncUseCase.syncOfflineArticles()
- [ ] updateCacheStats()
- [ ] `updateCacheStats()` implementieren
- [ ] Testen: ViewModel funktioniert
**Checklist**:
- [ ] File erstellt
- [ ] Properties definiert
- [ ] Dependencies injiziert
- [ ] setupBindings() fertig
- [ ] loadSettings() fertig
- [ ] syncNow() fertig
- [ ] updateCacheStats() fertig
- [ ] Test: ViewModel lädt Settings
---
### 4.2 OfflineSettingsView
**Neue Datei**: `readeck/UI/Settings/OfflineSettingsView.swift`
- [ ] Struct `OfflineSettingsView: View`
- [ ] @StateObject viewModel
- [ ] Body implementieren:
- [ ] Form mit Section
- [ ] Toggle: "Offline-Reading" gebunden an enabled
- [ ] If enabled:
- [ ] VStack: Erklärungstext (caption, secondary)
- [ ] VStack: Slider "Max. Artikel offline" (0-100, step 10)
- [ ] HStack: Anzeige aktueller Wert
- [ ] Toggle: "Bilder speichern"
- [ ] Button: "Jetzt synchronisieren" mit ProgressView
- [ ] If syncProgress: Text anzeigen (caption)
- [ ] If lastSyncDate: Text "Zuletzt synchronisiert: relative"
- [ ] If cachedArticlesCount > 0: HStack mit Stats
- [ ] Section header: "Offline-Reading"
- [ ] navigationTitle("Offline-Reading")
- [ ] onAppear: updateCacheStats()
- [ ] Testen: UI wird korrekt angezeigt
**Checklist**:
- [ ] File erstellt
- [ ] Form Structure erstellt
- [ ] Toggle für enabled
- [ ] Slider für maxUnreadArticles
- [ ] Toggle für saveImages
- [ ] Sync-Button mit Progress
- [ ] Stats-Anzeige
- [ ] UI-Preview funktioniert
- [ ] Test: Settings werden angezeigt
---
### 4.3 SettingsContainerView Integration
**Datei**: `readeck/UI/Settings/SettingsContainerView.swift`
- [ ] NavigationLink zu OfflineSettingsView hinzufügen
- [ ] Label mit "Offline-Reading" und Icon "arrow.down.circle"
- [ ] In bestehende Section (z.B. "Allgemein") einfügen
- [ ] Testen: Navigation funktioniert
**Checklist**:
- [ ] NavigationLink hinzugefügt
- [ ] Icon korrekt
- [ ] Navigation funktioniert
---
### 4.4 Factory erweitern
**Datei**: `readeck/UI/Factory/DefaultUseCaseFactory.swift`
- [ ] `makeOfflineSettingsViewModel()` implementieren:
- [ ] settingsRepository injecten
- [ ] offlineCacheSyncUseCase injecten
- [ ] OfflineSettingsViewModel instanziieren
- [ ] `makeSettingsRepository()` implementieren (private):
- [ ] SettingsRepository instanziieren
- [ ] `makeOfflineCacheSyncUseCase()` implementieren (private):
- [ ] bookmarksRepository injecten
- [ ] settingsRepository injecten
- [ ] OfflineCacheSyncUseCase instanziieren
- [ ] Testen: Dependencies werden korrekt aufgelöst
**Checklist**:
- [ ] makeOfflineSettingsViewModel() fertig
- [ ] makeSettingsRepository() fertig
- [ ] makeOfflineCacheSyncUseCase() fertig
- [ ] Test: ViewModel wird erstellt ohne Crash
---
### 4.5 MockUseCaseFactory erweitern (optional)
**Datei**: `readeck/UI/Factory/MockUseCaseFactory.swift`
- [ ] Mock-Implementierungen für Tests hinzufügen (falls nötig)
**Checklist**:
- [ ] Mocks erstellt (falls nötig)
---
## Phase 5: App Integration (Tag 3-4)
### 5.1 AppViewModel erweitern
**Datei**: `readeck/UI/AppViewModel.swift`
- [ ] `onAppStart()` Methode um Sync erweitern:
- [ ] Nach checkServerReachability()
- [ ] `syncOfflineArticlesIfNeeded()` aufrufen (ohne await!)
- [ ] `syncOfflineArticlesIfNeeded()` implementieren (private):
- [ ] SettingsRepository instanziieren
- [ ] Task.detached(priority: .background) starten
- [ ] Settings laden
- [ ] If shouldSyncOnAppStart:
- [ ] Logger.sync.info("Auto-sync triggered")
- [ ] syncUseCase holen via Factory
- [ ] await syncOfflineArticles()
- [ ] Testen: Auto-Sync bei App-Start (4h-Check)
**Checklist**:
- [ ] onAppStart() erweitert
- [ ] syncOfflineArticlesIfNeeded() implementiert
- [ ] Task.detached mit .background
- [ ] Kein await vor syncOfflineArticlesIfNeeded()
- [ ] Logger.sync integriert
- [ ] Test: App startet, Sync läuft im Hintergrund
---
### 5.2 BookmarksViewModel erweitern
**Datei**: `readeck/UI/Bookmarks/BookmarksViewModel.swift`
- [ ] `loadCachedBookmarks()` implementieren (private):
- [ ] bookmarksRepository.getCachedBookmarks() aufrufen
- [ ] If nicht leer:
- [ ] BookmarksPage erstellen mit gecachten Bookmarks
- [ ] bookmarks Property setzen
- [ ] hasMoreData = false
- [ ] errorMessage beibehalten (für Banner)
- [ ] Logger.viewModel.info
- [ ] `loadBookmarks()` erweitern:
- [ ] Im Network-Error catch block:
- [ ] Nach isNetworkError = true
- [ ] await loadCachedBookmarks() aufrufen
- [ ] Testen: Bei Network-Error werden gecachte Bookmarks geladen
**Checklist**:
- [ ] loadCachedBookmarks() implementiert
- [ ] loadBookmarks() erweitert
- [ ] Logger.viewModel integriert
- [ ] Test: Offline-Modus zeigt gecachte Artikel
---
### 5.3 BookmarksView erweitern
**Datei**: `readeck/UI/Bookmarks/BookmarksView.swift`
- [ ] `offlineBanner` View hinzufügen (private):
- [ ] HStack mit wifi.slash Icon
- [ ] Text "Offline-Modus Zeige gespeicherte Artikel"
- [ ] Styling: caption, secondary, padding, background
- [ ] `body` anpassen:
- [ ] ZStack durch VStack(spacing: 0) ersetzen
- [ ] If isNetworkError && bookmarks nicht leer:
- [ ] offlineBanner anzeigen
- [ ] Content darunter
- [ ] FAB als Overlay über VStack
- [ ] `shouldShowCenteredState` anpassen:
- [ ] Kommentar: Nur bei leer UND error
- [ ] return isEmpty && hasError
- [ ] Testen: Offline-Banner erscheint bei Network-Error mit Daten
**Checklist**:
- [ ] offlineBanner View erstellt
- [ ] body mit VStack umgebaut
- [ ] shouldShowCenteredState angepasst
- [ ] Test: Banner wird angezeigt im Offline-Modus
---
### 5.4 BookmarkDetailViewModel erweitern
**Datei**: `readeck/UI/BookmarkDetail/BookmarkDetailViewModel.swift`
- [ ] `loadArticle()` erweitern:
- [ ] Vor Server-Request:
- [ ] If let cachedHTML = bookmarksRepository.getCachedArticle(id:)
- [ ] articleHTML = cachedHTML
- [ ] isLoading = false
- [ ] Logger.viewModel.info("Loaded from cache")
- [ ] return
- [ ] Nach Server-Request (im Task.detached):
- [ ] Artikel optional cachen wenn saveImages enabled
- [ ] Testen: Gecachte Artikel laden sofort
**Checklist**:
- [ ] Cache-Check vor Server-Request
- [ ] Logger.viewModel integriert
- [ ] Optional: Background-Caching nach Load
- [ ] Test: Gecachte Artikel laden instant
---
## Phase 6: Testing & Polish (Tag 4-5)
### 6.1 Unit Tests
- [ ] OfflineSettings Tests:
- [ ] shouldSyncOnAppStart bei erstem Mal
- [ ] shouldSyncOnAppStart nach 3h (false)
- [ ] shouldSyncOnAppStart nach 5h (true)
- [ ] shouldSyncOnAppStart bei disabled (false)
- [ ] SettingsRepository Tests:
- [ ] Save & Load roundtrip
- [ ] Default values bei leerem UserDefaults
- [ ] BookmarksRepository Cache Tests:
- [ ] cacheBookmarkWithMetadata()
- [ ] getCachedArticle()
- [ ] hasCachedArticle()
- [ ] cleanupOldestCachedArticles()
- [ ] extractImageURLsFromHTML()
**Checklist**:
- [ ] OfflineSettings Tests geschrieben
- [ ] SettingsRepository Tests geschrieben
- [ ] BookmarksRepository Tests geschrieben
- [ ] Alle Tests grün
---
### 6.2 Integration Tests
- [ ] App-Start Sync:
- [ ] Erste Start: Sync läuft
- [ ] Zweiter Start < 4h: Kein Sync
- [ ] Nach 4h: Sync läuft
- [ ] Manual Sync:
- [ ] Button triggert Sync
- [ ] Progress wird angezeigt
- [ ] Success-Message erscheint
- [ ] Offline-Modus:
- [ ] Flugmodus aktivieren
- [ ] Gecachte Bookmarks werden angezeigt
- [ ] Offline-Banner erscheint
- [ ] Artikel lassen sich öffnen
- [ ] Cache Management:
- [ ] 20 Artikel cachen
- [ ] Stats zeigen 20 Artikel + Größe
- [ ] Cleanup funktioniert bei Limit-Überschreitung
**Checklist**:
- [ ] App-Start Sync getestet
- [ ] Manual Sync getestet
- [ ] Offline-Modus getestet
- [ ] Cache Management getestet
---
### 6.3 Edge Cases
- [ ] Netzwerk-Verlust während Sync:
- [ ] Partial success wird geloggt
- [ ] Status-Message korrekt
- [ ] Speicher voll:
- [ ] Fehlerbehandlung
- [ ] User-Benachrichtigung
- [ ] 100 Artikel Performance:
- [ ] Sync dauert < 2 Minuten
- [ ] App bleibt responsiv
- [ ] CoreData Migration:
- [ ] Alte App-Version → Neue Version
- [ ] Keine Datenverluste
- [ ] Kingfisher Cache:
- [ ] Bilder werden geladen
- [ ] Cache-Limit wird respektiert
**Checklist**:
- [ ] Netzwerk-Verlust getestet
- [ ] Speicher voll getestet
- [ ] 100 Artikel Performance OK
- [ ] Migration getestet
- [ ] Kingfisher funktioniert
---
### 6.4 Bug-Fixing & Polish
- [ ] Alle gefundenen Bugs gefixt
- [ ] Code-Review durchgeführt
- [ ] Logging überprüft (nicht zu viel, nicht zu wenig)
- [ ] UI-Polish (Spacing, Colors, etc.)
- [ ] Performance-Optimierungen falls nötig
**Checklist**:
- [ ] Alle Bugs gefixt
- [ ] Code reviewed
- [ ] Logging optimiert
- [ ] UI poliert
- [ ] Performance OK
---
## Final Checklist
### Funktionalität
- [ ] Offline-Reading Toggle funktioniert
- [ ] Slider für Artikel-Anzahl funktioniert
- [ ] Bilder-Toggle funktioniert
- [ ] Auto-Sync bei App-Start (4h-Check)
- [ ] Manual-Sync Button funktioniert
- [ ] Offline-Modus zeigt gecachte Artikel
- [ ] Offline-Banner wird angezeigt
- [ ] Cache-Stats werden angezeigt
- [ ] Last-Sync-Date wird angezeigt
- [ ] Background-Sync mit niedriger Priority
- [ ] Kingfisher cached Bilder
- [ ] FIFO Cleanup funktioniert
### Code-Qualität
- [ ] Alle neuen Files erstellt
- [ ] Alle Protokolle definiert
- [ ] Alle Implementierungen vollständig
- [ ] Logging überall integriert
- [ ] Error-Handling implementiert
- [ ] Keine Compiler-Warnings
- [ ] Keine Force-Unwraps
- [ ] Code dokumentiert (Kommentare wo nötig)
### Tests
- [ ] Unit Tests geschrieben
- [ ] Integration Tests durchgeführt
- [ ] Edge Cases getestet
- [ ] Performance getestet
- [ ] Alle Tests grün
### Dokumentation
- [ ] Implementierungsplan vollständig
- [ ] Alle Checkboxen abgehakt
- [ ] Gefundene Issues dokumentiert
- [ ] Nächste Schritte (Stufe 2) überlegt
---
## Commit & PR
- [ ] Alle Änderungen commited
- [ ] Commit-Messages aussagekräftig
- [ ] Branch gepusht
- [ ] PR erstellt gegen `develop`
- [ ] PR-Beschreibung vollständig:
- [ ] Was wurde implementiert
- [ ] Wie testen
- [ ] Screenshots (Settings-UI)
- [ ] Known Issues (falls vorhanden)
---
## Notes & Issues
### Gefundene Probleme
_(Hier während der Implementation eintragen)_
### Offene Fragen
_(Hier während der Implementation eintragen)_
### Verbesserungsideen für Stufe 2
_(Hier sammeln)_
---
*Erstellt: 2025-11-01*
*Letztes Update: 2025-11-01*

File diff suppressed because it is too large Load Diff

304
documentation/claude.md Normal file
View File

@ -0,0 +1,304 @@
# CLAUDE.md - readeck iOS Project Documentation
## Project Overview
**readeck iOS** is a native iOS client for [readeck](https://readeck.org) bookmark management. The app provides a clean, native iOS interface for managing bookmarks with features like swipe actions, search, tagging, and reading progress tracking.
### Key Information
- **Platform:** iOS (iPhone + iPad)
- **Language:** Swift
- **UI Framework:** SwiftUI
- **Architecture:** MVVM + Clean Architecture (3-layer: UI/Domain/Data)
- **Database:** CoreData
- **Dependencies:** Swift Package Manager
- **License:** MIT
## Architecture Summary
The project follows Clean Architecture with custom dependency injection:
```
UI Layer (SwiftUI Views + ViewModels)
Domain Layer (Use Cases + Repository Protocols + Models)
Data Layer (Repository Implementations + API + CoreData)
```
### Core Components
- **Custom DI:** Protocol-based factory pattern (no external frameworks)
- **MVVM Pattern:** ViewModels handle business logic, Views handle presentation
- **Use Cases:** Single-responsibility business logic encapsulation
- **Repository Pattern:** Data access abstraction with protocols
## Project Structure
```
readeck/
├── UI/ # SwiftUI Views & ViewModels
│ ├── Bookmarks/ # Main bookmark list
│ ├── BookmarkDetail/ # Article reader
│ ├── AddBookmark/ # Create new bookmarks
│ ├── Search/ # Search functionality
│ ├── Settings/ # App configuration
│ ├── Labels/ # Tag management
│ ├── Menu/ # Navigation & tabs
│ ├── SpeechPlayer/ # Text-to-speech
│ └── Components/ # Reusable UI components
├── Domain/
│ ├── Model/ # Core business models
│ ├── UseCase/ # Business logic
│ ├── Protocols/ # Repository interfaces
│ └── Error/ # Custom error types
├── Data/
│ ├── API/ # Network layer & DTOs
│ ├── Repository/ # Data access implementations
│ ├── CoreData/ # Local database
│ └── Utils/ # Helper utilities
└── Localizations/ # i18n strings
├── Base.lproj/
├── en.lproj/
└── de.lproj/
```
## Key Features
### Implemented Features
- ✅ Browse bookmarks (All, Unread, Favorites, Archive by type)
- ✅ Share Extension for adding URLs from Safari/other apps
- ✅ Swipe actions for quick bookmark management
- ✅ Native iOS design with Dark Mode support
- ✅ Full iPad Support with Multi-Column Split View
- ✅ Font customization in reader
- ✅ Article view with reading time and word count
- ✅ Search functionality
- ✅ Tag/label management
- ✅ Reading progress tracking
- ✅ Offline support with auto-sync when reconnected
- ✅ Text-to-speech (Read Aloud feature)
### Planned Features (v1.1.0)
- ⏳ Bookmark filtering and sorting options
- ⏳ Collection management
- ⏳ Custom themes
- ⏳ Text highlighting in articles
- ⏳ Multiple selection for bulk actions
## Development Setup
### Requirements
- Xcode 15.0+
- iOS 17.0+ deployment target
- Swift Package Manager (dependencies auto-resolved)
### Key Dependencies
- **netfox:** Network debugging (debug builds only)
- **RswiftLibrary:** Resource management
### Build Configurations
- **Debug:** Includes netfox for network debugging
- **Release:** Production-ready build
- **URLShare Extension:** Share extension target
## Localization (Weblate Integration)
### Current Setup
The project has been converted from Apple's String Catalog (.xcstrings) to traditional .strings format for Weblate compatibility:
```
readeck/Localizations/
├── Base.lproj/Localizable.strings # Source language (English)
├── en.lproj/Localizable.strings # English localization
└── de.lproj/Localizable.strings # German localization
```
### Weblate Configuration
When setting up Weblate:
- **File mask:** `readeck/Localizations/*.lproj/Localizable.strings`
- **Monolingual base:** `readeck/Localizations/Base.lproj/Localizable.strings`
- **File format:** "iOS Strings (UTF-8)"
- **Repository:** Connect to main Git repository
### Adding New Languages
1. Create new `.lproj` directory (e.g., `fr.lproj/`)
2. Copy `Base.lproj/Localizable.strings` to new directory
3. Weblate will automatically detect and manage translations
## App State Management & Navigation
### Setup Flow & Authentication
The app uses a sophisticated setup and authentication system:
**Initial Setup Detection:**
- `AppViewModel.hasFinishedSetup` controls the main app flow
- `readeckApp.swift:19` determines whether to show setup or main app
- Setup status is persisted via `SettingsRepository.hasFinishedSetup`
**Authentication & Keychain Management:**
- `KeychainHelper` (singleton) securely stores sensitive credentials:
- Server endpoint (`readeck_endpoint`)
- Username (`readeck_username`)
- Password (`readeck_password`)
- Authentication token (`readeck_token`)
- Access Group: `8J69P655GN.de.ilyashallak.readeck` for app group sharing
- Automatic logout on 401 responses via `AppViewModel.handleUnauthorizedResponse()`
**Device-Specific Navigation:**
The app automatically adapts its navigation structure based on device type:
```swift
// MainTabView.swift determines layout
if UIDevice.isPhone {
PhoneTabView() // Tab-based navigation
} else {
PadSidebarView() // Sidebar + split view navigation
}
```
**Navigation Patterns:**
- **iPhone:** `PhoneTabView` - Traditional tab bar with "More" tab for additional features
- **iPad:** `PadSidebarView` - NavigationSplitView with sidebar, content, and detail panes
- Both share the same underlying ViewModels and business logic
**Key Navigation Components:**
- `SidebarTab` enum defines all available sections
- Main tabs: `.all`, `.unread`, `.favorite`, `.archived`
- More tabs: `.search`, `.article`, `.videos`, `.pictures`, `.tags`, `.settings`
- Consistent routing through `tabView(for:)` methods in both variants
## Key Architectural Decisions
### 1. Custom Dependency Injection
- **Why:** Avoid external framework dependencies, full control
- **How:** Protocol-based factory pattern in `DefaultUseCaseFactory`
- **Benefit:** Easy testing with mock implementations
### 2. Repository Pattern
- **Domain Layer:** Defines protocols (e.g., `PBookmarksRepository`)
- **Data Layer:** Implements protocols (e.g., `BookmarksRepository`)
- **Benefit:** Clean separation between business logic and data access
### 3. Use Cases
- Single-responsibility classes for each business operation
- Examples: `CreateBookmarkUseCase`, `GetBookmarksUseCase`
- **Benefit:** Testable, reusable business logic
### 4. SwiftUI + MVVM
- ViewModels as `@ObservableObject` classes
- Views are pure presentation layer
- State management through ViewModels
## Testing Strategy
### Current Test Coverage
- **Unit Tests:** `readeckTests/` (basic coverage)
- **UI Tests:** `readeckUITests/` (smoke tests)
### Testing Philosophy
- Protocol-based DI enables easy mocking
- Use Cases can be tested in isolation
- Repository implementations tested with real/mock data sources
## Distribution
### TestFlight Beta
- Public beta available via TestFlight
- Link: `https://testflight.apple.com/join/cV55mKsR`
- Regular updates with new features
### Release Process
- Uses fastlane for build automation
- Automated screenshot generation
- Version management in Xcode project
## API Integration
### readeck Server API
- RESTful API communication
- DTOs in `Data/API/DTOs/`
- Authentication via username/password
- Token management with automatic refresh
- Support for local network addresses (development)
### Key API Operations
- User authentication
- Bookmark CRUD operations
- Tag/label management
- Article content fetching
- Progress tracking
## Offline Support
### Local Storage
- CoreData for offline bookmark storage
- Automatic sync when connection restored
- Queue system for offline operations
- Conflict resolution strategies
### Sync Strategy
- Background sync when app becomes active
- User-initiated sync option
- Visual indicators for sync status
## Performance Considerations
### Memory Management
- Lazy loading of bookmark content
- Image caching for article thumbnails
- Proper SwiftUI view lifecycle management
### Network Optimization
- Background download of article content
- Request batching where possible
- Retry logic for failed requests
## Contributing Guidelines
### Code Style
- Follow Swift API Design Guidelines
- Use SwiftUI best practices
- Maintain separation of concerns between layers
### New Feature Development
1. Define domain models in `Domain/Model/`
2. Create use case in `Domain/UseCase/`
3. Implement repository in `Data/Repository/`
4. Create ViewModel in appropriate UI folder
5. Build SwiftUI view
6. Update factory for DI wiring
### Git Workflow
- Main branch: `main`
- Development branch: `develop`
- Feature branches: `feature/feature-name`
- Commit format: `feat:`, `fix:`, `docs:`, etc.
## Troubleshooting
### Common Issues
1. **Build Errors:** Ensure Xcode 15.0+ and clean build folder
2. **Network Issues:** Check server URL and credentials
3. **CoreData Migrations:** May need to reset data during development
4. **Localization:** Ensure .strings files are properly formatted
### Development Tips
- Use netfox in debug builds to monitor API calls
- Check logging configuration in debug settings
- Test both iPhone and iPad layouts
- Verify share extension functionality
## Security Considerations
### Data Protection
- Keychain storage for user credentials
- No sensitive data in UserDefaults
- Secure network communication (HTTPS enforced for external domains)
### Privacy
- No analytics or tracking libraries
- Local data storage only
- User controls all data sync
---
*This documentation is maintained alongside the codebase. Update this file when making architectural changes or adding new features.*

View File

@ -0,0 +1,45 @@
<section>
<div id="iK.lpVA.attachment_641518"><p><img decoding="async" src="https://keep.mnk.any64.de/bm/TH/THG5skZihVByEr5P5eTeBV/_resources/2wRXN15RYtCJ4JFbR8kMAy.jpg" alt="Sport vor der Arbeit fördert die Produktivität. (Foto: Jacob Lund / Shutterstock)" width="930" height="620" loading="lazy"/></p><p>Sport vor der Arbeit fördert die Produktivität. (Foto: <a href="http://www.shutterstock.com/gallery-163108p1.html" rel="nofollow noopener noreferrer">Jacob Lund</a> / Shutterstock)</p></div>
<p>Zu viele von uns sind zu sehr verplant, übermäßig vernetzt und übermäßig stimuliert von all dem Lärm, den Unterbrechungen und der Komplexität der heutigen Gesellschaft. Der Preis dieses Lebensstils? Du erreichst die letzte Stunden deines letzten Tages und merkst, dass du dein größtes Potenzial für deine sinnlosesten Aktivitäten vergeudet hast.</p>
<p>Herausragende Performer, Spitzenleute und Welt-Erschaffer spielen in einer anderen Liga. Die Elon Musks, Mark Zuckerbergs, große Künstler und Top-Wissenschaftler planen ihre Tage alle mit komplett anderer Geisteshaltung und Ritualen als jene, die im Hamsterrad gefangen sind.</p>
<p>Als privater Coach von vielen der erfolgreichsten Unternehmer auf diesem Planeten und Gründer von The Titan Summit, das jeden Dezember in Toronto Ultra-Performer von mehr als 43 Nationen vier Tage für ein Elite-Training für <a title="Weitere News zu Produktivität" href="https://t3n.de/tag/produktivitaet/" rel="nofollow noopener noreferrer">Produktivität</a>, Business-Acceleration und Lifestyle-Optimierung zusammenbringt, habe ich mit eigenen Augen beobachtet, wie Menschen, die in einer Woche mehr schaffen als die meisten in einem Vierteljahr, ihre Ergebnisse erzielen. Ich habe außerdem eine komplette Methodik für exponentiale Produktivität entwickelt, die ich den Teilnehmern bei diesem Event beibringe.</p>
<p>Hier sind vier der wegweisenden Elemente meines Ansatzes:</p>
<p id="iK.lpVA.1_Die_20/20/20Formel">
</p><h2>1. Die 20/20/20-Formel</h2>
<p></p>
<p>Die Art, wie du deinen Tag kraftvoll startest, bestimmt, wie produktiv du ihn gestaltest. Reserviere die ersten 60 Minuten für persönliche Vorbereitung. Wie die spartanischen Krieger sagten: „Schwitze mehr beim Trainieren, dann wirst du im Krieg weniger bluten.“ Verbringe deine ersten 20 Minuten mit intensivem Sport. Das Schwitzen setzt BDNF frei, einen Botenstoff, der tatsächlich neurale Verbindungen wachsen lässt. Ein Workout produziert auch Dopamin (der Neurotransmitter für Motivation) und Serotonin, welches gute Laune macht.</p>
<p>Schau dir im nächsten 20-Minuten-Slot deinen Jahresplan an und denk gründlich über deine Ziele für dieses Quartal nach. Klarheit geht der Meisterschaft voraus, und diese Übung wird den Tag über deine Konzentration verstärken.</p>
<p>Investiere die letzten 20 Minuten dieses Morgens für regelmäßiges Lernen. Lies zum Beispiel Autobiografien großer Persönlichkeiten, hör dir einen Podcast über das Führen an oder lade dir die Lektionen des Vortages in dein Journal herunter.</p>
<p id="iK.lpVA.2_Die_90/90/1Regel">
</p><h2>2. Die 90/90/1-Regel</h2>
<p></p>
<p>Allein diese Angewohnheit hat meinen Kunden einen gewaltigen Mehrwert verschafft. Kurz gesagt: Widme dich an den nächsten 90 Tagen die ersten 90 Minuten deines Arbeitstages deiner einzigen, wichtigsten Chance, der einen Sache, die, wenn du sie fehlerlos durchführst, alles aufgehen lässt.</p>
<p>Durchschnittliche Mitarbeiter kommen zur Arbeit und checken ihre Mails oder surfen im Netz. Für den echten Anführer ist die Ankunft im Büro der Beginn der Showtime. Sie begreifen, dass das Entwickeln einer Besessenheit für ihre entscheidenden paar Prioritäten legendäre Resultate freisetzt.</p>
<p id="iK.lpVA.3_Die_60/10Methode">
</p><h2>3. Die 60/10-Methode</h2>
<p></p>
<p>Gute Studien belegen, dass die besten Athleten der Welt das nicht dafür waren, was sie in ihrem Sport leisteten, sondern wie effektiv sie sich erholten. Es waren beispielsweise die Rituale, welche die Star-Tennisspieler zwischen ihren Punkten vollzogen, die sie zu Stars machten. Was die russischen Gewichtheber so unschlagbar machte, war ihr Verhältnis von Arbeit zu Erholungspausen.</p>
<p>Also, stell dir einen Wecker auf 60 Minuten und schalte in dieser Zeitspanne alle technischen Geräte aus, schließ deine Tür und tauche mit voller Wucht in ein Projekt ein, das wichtig ist. Erhol dich dann mit einer echten Pause wie Walking, Musikhören oder Lesen. Probiere diese Vorgehensweise mal für einen Monat aus und spüre den Nutzen.</p>
<p id="iK.lpVA.4_Finde_deinen_Kreis_von_Genies">
</p><h2>4. Finde deinen Kreis von Genies</h2>
<p></p>
<p>Verhaltensforscher haben das Phänomen des „emotionalen Ansteckungseffekts“ entdeckt. Dieses beschreibt, dass wir unbewusst die Glaubenssätze, Gefühle und Verhalten der Leute übernehmen, mit denen wir die meiste Zeit verbringen.</p>
<p>Du möchtest ultra-fit werden? Einer der besten Wege dahin ist es, dich einer Laufgruppe anzuschließen oder dich mit Athleten anzufreunden. Du möchtest glücklicher sein? Dann streich die Energieräuber und Meckerer aus deinem Leben. Du bist darauf aus, eine echte Weltklassefirma aufzubauen?  Dann fang an, viel mehr Zeit mit denen zu verbringen, die das bereits getan haben. Ihre Geisteshaltung und Lebensart wird dich mit der Zeit automatisch beeinflussen. Und mit Leuten Umgang zu haben, deren Leben du gern führen würdest, zeigt dir, was alles möglich ist. Und wenn du erstmal mehr weißt, kannst du auch mehr erreichen.</p>
<p>Schlussendlich möchte ich dich noch an die Endlichkeit des Lebens erinnern. Sogar das längste ist ein relativ kurzer Ritt. Du schuldest es dem Talent, mit dem du geboren wurdest, dem Team, das du führst, der Familie, die du liebst und der Welt, die danach verlangt, dass du deine Größe zeigst, um das zu Nötige zu tun, exponentielle Produktivität zu erlangen.</p>
<p>Hoffentlich nimmst du diese von mir geteilte Methodik an, um deinem Führungspotenzial gerecht zu werden.</p>
<p>Wie immer hoffe ich, dass diese Zeilen dir echten Mehrwert geliefert haben und dich in die Lage versetzen, den Rest dieses Jahres  „in der kostbaren Luft vollständiger Meisterschaft“ zu vollenden.</p>
<p>Deine Partner sind Führungsqualität und Erfolg,</p>
<p><em>Dieser Artikel erschien zuerst auf <a href="https://medium.com/swlh/the-methods-for-superhuman-productivity-de4452af7cfb" rel="nofollow noopener noreferrer">Medium.com</a>. Übersetzung: Anja Braun.</em></p>
<p><img width="1" height="1" alt="" loading="lazy"/></p>
</section>

66
documentation/tabbar2.md Normal file
View File

@ -0,0 +1,66 @@
# Prompt für KI-Agent zur Überprüfung und Modernisierung einer bestehenden TabView mit NavigationStacks in SwiftUI (iOS 26)
---
## Ziel
Prüfe eine existierende SwiftUI-App mit bestehender `TabView` und `NavigationStacks` auf iOS 26-Konformität und korrigiere sie gegebenenfalls. Hauptpunkte:
- Die `TabView` soll mit moderner iOS 26 Tab-API aufgebaut sein, d.h. Tabs als eigenständige `Tab`-Views und KEIN `.tabItem` mehr verwenden.
- Jeder Tab soll eine eigene `NavigationStack` mit eigenem `NavigationPath` haben, um den Navigationszustand pro Tab unabhängig zu verwalten.
- Der Tab-Auswahl-Binding (`@State`) und `.tag()`-Zuweisungen müssen korrekt gesetzt sein.
- Der neue Such-Tab soll als `Tab(role: .search)` implementiert sein mit einem eigenen Suchfeld via `.searchable()`.
- Navigationstitel, Suchfunktion und Navigationslinks müssen in der jeweiligen NavigationStack-Umgebung eingebettet sein.
- Die TabBar soll beim Tiefennavigieren in einem Tab sichtbar bleiben, außer es gibt ein explizites Ausblenden.
- Eventuelle veraltete oder falsche Patterns wie `.tabItem` oder kombiniert verwendete `NavigationView` außerhalb der Stacks sollen korrigiert werden.
- Alle Subviews sollen modular organisiert sein und keine Globalzustände die Navigation verwalten.
---
## Prüffragen für den Agenten
1. Nutzt die `TabView` die neue Form mit `Tab` als Container pro Tab?
2. Werden für jeden Tab eigene `NavigationStack`s und `NavigationPath`s verwendet?
3. Sind `.tag()` und `selection` in `TabView` korrekt implementiert?
4. Ist der Such-Tab mit `Tab(role: .search)` sauber getrennt und die Suche mit `.searchable()` eingebunden?
5. Werden veraltete `.tabItem` Modifier vollständig entfernt?
6. Bleibt die TabBar sichtbar beim Navigieren in den Stacks, außer bewusst ausgeblendet?
7. Wird State sauber und lokal in den jeweiligen Views verwaltet?
8. Gibt es keine vermischten oder redundanten NavigationView/Stacks?
9. Werden Navigationsziele übersichtlich in Subviews ausgelagert?
10. Ist der gesamte Code idiomatisch und an die iOS 26 SwiftUI-Standards angepasst?
---
## Ausgabeformat
Der Agent soll die App prüfen, Fehler auflisten, Korrekturen vorschlagen und wenn möglich direkt umgesetzten SwiftUI-Code erzeugen, der:
- Komplette TabView mit Tabs als `Tab`
- Jeweils eigene NavigationStack mit NavigationPath
- Such-Tab mit `Tab(role: .search)` und suchbarer Navigation
- Keine `.tabItem` oder deprecated Patterns enthält
- Klar strukturiert und modular ist
---
## Beispiel-Ausschnitt zur Referenz
```swift
TabView(selection: $selectedTab) {Tab(“Home”, systemImage: “house”) {NavigationStack(path: $homePath) {HomeView()}}.tag(Tab.home)
Tab(role: .search) {
NavigationStack(path: $searchPath) {
SearchView()
.searchable(text: $searchText)
.navigationTitle("Search")
}
}
.tag(Tab.search)
// weitere Tabs...
}
```
---