# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Nextcloud Cookbook iOS Client — a native iOS/iPadOS/macOS (Mac Catalyst) client for the [Nextcloud Cookbook](https://github.com/nextcloud/cookbook) server application. Built entirely in Swift and SwiftUI. Licensed under GPLv3.

**This is not a standalone app.** It requires a Nextcloud server with the Cookbook app installed.

## Build & Run

This is an Xcode project (no workspace, no CocoaPods). Open `Nextcloud Cookbook iOS Client.xcodeproj` in Xcode.

```bash
# Build from command line
xcodebuild -project "Nextcloud Cookbook iOS Client.xcodeproj" \
  -scheme "Nextcloud Cookbook iOS Client" \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  build

# Run tests (note: tests are currently boilerplate stubs with no real coverage)
xcodebuild -project "Nextcloud Cookbook iOS Client.xcodeproj" \
  -scheme "Nextcloud Cookbook iOS Client" \
  -destination 'platform=iOS Simulator,name=iPhone 16' \
  test
```

- **Deployment target**: iOS 16.4
- **Swift version**: 5.0
- **Targets**: iPhone and iPad, Mac Catalyst enabled

## Dependencies (SPM)

Only two third-party packages, managed via Swift Package Manager integrated in the Xcode project:

| Package | Purpose |
|---------|---------|
| [SwiftSoup](https://github.com/scinfu/SwiftSoup.git) (2.6.1) | HTML parsing for client-side recipe scraping |
| [TPPDF](https://github.com/techprimate/TPPDF.git) (2.4.1) | PDF generation for recipe export |

## Architecture

### Central State Pattern

The app uses a **centralized `AppState` ObservableObject** (`AppState.swift`) as the primary ViewModel, injected via `.environmentObject()` into the SwiftUI view hierarchy. `AppState` owns:
- All data (categories, recipes, recipe details, images, timers)
- All CRUD operations against the Cookbook API
- A `FetchMode` enum (`preferLocal`, `preferServer`, `onlyLocal`, `onlyServer`) that governs the data-fetching strategy (server-first vs local-first)
- Local persistence via `DataStore`

Additional ViewModels exist as nested classes within their views (`RecipeTabView.ViewModel`, `SearchTabView.ViewModel`) or as standalone classes (`RecipeEditViewModel`, `GroceryList`).

### Data Flow

```
SwiftUI Views
  ├── @EnvironmentObject appState: AppState
  ├── @EnvironmentObject groceryList: GroceryList
  └── Per-view @StateObject ViewModels
        │
        ▼
AppState
  ├── cookbookApi (CookbookApiV1 — static methods) → ApiRequest → URLSession
  ├── DataStore (file-based JSON persistence in Documents directory)
  └── UserSettings.shared (UserDefaults singleton)
```

### Network Layer

- `CookbookApi` protocol defines all endpoints; `CookbookApiV1` is the concrete implementation with all `static` methods.
- The global `cookbookApi` constant (`CookbookApi.swift:14`) resolves the API version at launch.
- `ApiRequest` is a generic HTTP request builder using `URLSession.shared.data(for:)` with HTTP Basic Auth.
- `NextcloudApi` handles Nextcloud-specific auth (Login Flow v2 and token-based login).

### Persistence

- **`DataStore`**: File-based persistence using `JSONEncoder`/`JSONDecoder` writing to the app's Documents directory. No Core Data or SQLite.
- **`UserSettings`**: Singleton wrapping `UserDefaults` for all user preferences and credentials.
- **Image caching**: Two-tier — in-memory dictionary in `AppState` + on-disk base64-encoded PNG files via `DataStore`.

### Key Source Directories

```
Nextcloud Cookbook iOS Client/
├── Data/           # Models (Category, Recipe, RecipeDetail, Nutrition) + DataStore + UserSettings
├── Models/         # RecipeEditViewModel
├── Network/        # ApiRequest, NetworkError, CookbookApi protocol + V1, NextcloudApi
├── Views/
│   ├── Tabs/       # Main tab views (RecipeTab, SearchTab, GroceryListTab)
│   ├── Recipes/    # Recipe detail, list, card, share, timer views
│   ├── RecipeViewSections/  # Decomposed recipe detail sections (ingredients, instructions, etc.)
│   ├── Onboarding/ # Login flows (V2LoginView, TokenLoginView)
│   └── ReusableViews/
├── Extensions/     # Color, Date, JSONCoder, Logger extensions
├── Util/           # Alerts, DurationComponents (ISO 8601 PT parser), JsonAny, NumberFormatter
├── RecipeExport/   # PDF, text, JSON export via RecipeExporter
└── RecipeImport/   # HTML scraping via SwiftSoup (schema.org ld+json Recipe data)
```

## Localization

Four languages supported via `Localizable.xcstrings`: English, German, Spanish, French. Spanish and French are mostly machine-translated.

## Notable Design Decisions

- The `cookbookApi` global is resolved once at launch based on `UserSettings.shared.cookbookApiVersion` and uses static protocol methods, which makes dependency injection and unit testing difficult.
- Server credentials (username, token, authString) are stored in `UserDefaults` via `UserSettings`, not in Keychain.
- No `.gitignore` file exists in the repository.
- No CI/CD, no linting tools, and no meaningful test coverage.

## Workflow

- Do not run `xcodebuild` directly. Ask the user to build manually in Xcode and report the results back.
