Files
Nextcloud-Cookbook-iOS/CLAUDE.md
Hendrik Hogertz 527acd2967 Raise deployment target to iOS 18 and modernize SwiftUI APIs
Adopt modern SwiftUI patterns now that the minimum target is iOS 18:
NavigationStack, .toolbar, .tint, new Tab API with sidebarAdaptable
style, and remove iOS 17 availability checks. Add Liquid Glass effect
support for iOS 26 in TimerView and fix an optional interpolation
warning in AppState.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-14 23:14:57 +01:00

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