Introduce a GroceryListManager facade that delegates to either the existing in-app GroceryList or a new RemindersGroceryStore backed by EventKit. Users choose the mode in Settings; when Reminders mode is active the Grocery List tab is hidden. Recipe-to-reminder grouping uses a local mapping file (reminder_mappings.data) instead of polluting the reminder's notes field, with automatic pruning when reminders are deleted externally. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
115 lines
5.2 KiB
Markdown
115 lines
5.2 KiB
Markdown
# 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 18
|
|
- **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.
|