Files
libnovel/ios/AGENTS.md
Admin 7413313100
All checks were successful
CI / Scraper / Lint (push) Successful in 10s
CI / Scraper / Test (push) Successful in 14s
Release / Scraper / Test (push) Successful in 18s
CI / Scraper / Lint (pull_request) Successful in 18s
Release / UI / Build (push) Successful in 23s
CI / Scraper / Test (pull_request) Successful in 15s
CI / UI / Build (pull_request) Successful in 32s
Release / Scraper / Docker (push) Successful in 55s
CI / Scraper / Docker Push (pull_request) Has been skipped
CI / UI / Docker Push (pull_request) Has been skipped
CI / Scraper / Docker Push (push) Successful in 1m5s
Release / UI / Docker (push) Successful in 1m12s
iOS CI / Build (push) Successful in 4m18s
iOS CI / Build (pull_request) Successful in 4m25s
iOS CI / Test (push) Successful in 8m11s
iOS CI / Test (pull_request) Successful in 8m21s
fix: update integration_test.go to match server.New signature (version, commit args)
2026-03-14 14:25:46 +05:00

5.0 KiB

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