# LibNovel iOS App SwiftUI app targeting iOS 17+. Consumes the Go scraper HTTP API for books, chapters, and audio. Uses MinIO presigned URLs for media playback and downloads. ## Project Structure ``` ios/LibNovel/LibNovel/ ├── App/ # LibNovelApp.swift, ContentView.swift, RootTabView.swift ├── Models/ # Models.swift (all domain types) ├── Networking/ # APIClient.swift (URLSession-based HTTP client) ├── Services/ # AudioPlayerService, AudioDownloadService, AuthStore, │ # BookVoicePreferences, NetworkMonitor ├── ViewModels/ # One per view/feature (HomeViewModel, BrowseViewModel, etc.) ├── Views/ │ ├── Auth/ # AuthView │ ├── BookDetail/ # BookDetailView, CommentsView │ ├── Browse/ # BrowseView (infinite scroll shelves) │ ├── ChapterReader/ # ChapterReaderView, DownloadAudioButton │ ├── Common/ # CommonViews (shared reusable components) │ ├── Components/ # OfflineBanner │ ├── Downloads/ # DownloadsView, DownloadQueueButton │ ├── Home/ # HomeView │ ├── Library/ # LibraryView (2-col grid, filters) │ ├── Player/ # PlayerViews (floating FAB, compact, full-screen) │ ├── Profile/ # ProfileView, VoiceSelectionView, UserProfileView, etc. │ └── Search/ # SearchView └── Extensions/ # NavDestination.swift, String+App.swift, Color+App.swift ``` ## iOS / Swift Conventions - **Deployment target**: iOS 17.0 — use iOS 17+ APIs freely. - **Observable pattern**: The codebase currently uses `@StateObject` / `ObservableObject` / `@Published`. When adding new types, prefer the **`@Observable` macro** (iOS 17+) over `ObservableObject`. Do not refactor existing types unless explicitly asked. - **Navigation**: Use `NavigationStack` (not `NavigationView`). Use `.navigationDestination(for:)` for type-safe routing. - **Concurrency**: Use `async/await` and structured concurrency. Avoid callback-based APIs and `DispatchQueue.main.async` — prefer `@MainActor` or `await MainActor.run`. - **State management**: Prefer `@State` + `@Binding` for local UI state. Use environment objects for app-wide services (authStore, audioPlayer, downloadService, networkMonitor). - **SwiftData**: Not currently used. Do not introduce SwiftData without discussion. - **SF Symbols**: Use `Image(systemName:)` for icons. No emoji in UI unless already present. ## Key Patterns - **Download keys**: Use `::` as separator (e.g., `"slug::chapter-1::voice"`), never `-`. Slugs contain hyphens. - **Voice fallback chain**: book override → global default → `"af_bella"`. See `BookVoicePreferences.voiceWithFallback()`. - **Offline handling**: Wrap view bodies in `VStack` with `OfflineBanner` at top. Use `NetworkMonitor` (environment object) to gate network calls. Suppress network errors silently when offline via `ErrorAlertModifier`. - **Audio playback priority**: local file → MinIO presigned URL → trigger TTS generation. - **Progress display**: Show decimal % when < 10% (e.g., "3.4%"), rounded when >= 10% (e.g., "47%"). - **Cover images**: Always proxy via `/api/cover/{domain}/{slug}` — never link directly to source. ## Networking `APIClient.swift` wraps all Go scraper API calls. When adding new endpoints: 1. Add a method to `APIClient`. 2. Keep error handling consistent — throw typed errors, let ViewModels catch and set `errorMessage`. 3. All requests are relative to `SCRAPER_API_URL` (configured at build time via xcconfig or environment). ## Using Documentation Tools When writing or reviewing SwiftUI/Swift code: - Use `context7` to look up current Apple SwiftUI/Swift documentation before implementing anything non-trivial. Apple's APIs evolve fast — do not rely on training data alone. - Use `gh_grep` to find real-world Swift patterns when unsure how something is typically implemented. Example prompts: - "How does `.searchable` work in iOS 17? use context7" - "Show me examples of `@Observable` with async tasks. use context7" - "How do other apps implement background URLSession downloads in Swift? use gh_grep" ## UI/UX Skill For any iOS view work, always load the `ios-ux` skill at the start of the task: ``` skill({ name: "ios-ux" }) ``` This skill defines the full design system, animation rules, haptic feedback policy, accessibility checklist, performance guidelines, and offline handling requirements. It also governs how to handle screenshot-based reviews (analyze → suggest → confirm before applying). ## What to Avoid - `NavigationView` — deprecated, use `NavigationStack` - `ObservableObject` / `@Published` for new types — prefer `@Observable` - `DispatchQueue.main.async` — prefer `@MainActor` - Force unwrapping optionals - Hardcoded color literals — use `Color+App.swift` extensions or semantic colors - Adding new dependencies (SPM packages) without discussion