Files
Nextcloud-Cookbook-iOS/CLAUDE.md
Hendrik Hogertz 98c82dc537 Add Apple Reminders integration for grocery list with local mapping persistence
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>
2026-02-15 02:54:52 +01:00

5.2 KiB

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 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.

# 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 (2.6.1) HTML parsing for client-side recipe scraping
TPPDF (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.