# 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
