CLAUDE.md

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

Project

Klepon is a SwiftUI guide to Indonesian dishes, ingredients, and food traditions. It supports iOS, macOS, tvOS, visionOS, and watchOS and uses the Ed Swift SDK (the Onde-powered on-device agent) for private on-device AI answers.

Project structure

The .xcodeproj is generated by XcodeGen from project.yml. Modify project.yml to change targets, sources, dependencies, or build settings, then regenerate:

xcodegen generate

Source layout: - Klepon/ — shared source used by iOS, macOS, tvOS, and visionOS (conditional compilation via #if os(...)) - KleponTV/App/ — tvOS-only entry point; replaces KleponApp.swift and AppRootView.swift with a UIKit AppDelegate + SceneDelegate + KleponTabBarController - KleponWatch/ — watchOS app; shares only GuideEntry.swift, KleponColor.swift, and the bundled content JSON - KleponTVTopShelfExtension/ — tvOS Top Shelf extension - KleponMac/, KleponVision/ — target-specific Info.plist and entitlements - Klepon/Resources/Content/ — bundled JSON content (guide_entries.json, collections.json) - Configs/ — xcconfig files for build settings and secrets

Build commands

Build each scheme without signing (for CI or local verification):

# iOS Simulator
xcodebuild -project Klepon.xcodeproj -scheme Klepon -destination 'generic/platform=iOS Simulator' CODE_SIGNING_ALLOWED=NO build

# macOS
xcodebuild -project Klepon.xcodeproj -scheme 'Klepon Mac' -destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO build

# tvOS Simulator
xcodebuild -project Klepon.xcodeproj -scheme 'Klepon TV' -destination 'generic/platform=tvOS Simulator' CODE_SIGNING_ALLOWED=NO build

# visionOS Simulator
xcodebuild -project Klepon.xcodeproj -scheme 'Klepon Vision' -destination 'generic/platform=visionOS Simulator' CODE_SIGNING_ALLOWED=NO build

# watchOS Simulator
xcodebuild -project Klepon.xcodeproj -scheme 'Klepon Watch' -destination 'generic/platform=watchOS Simulator' CODE_SIGNING_ALLOWED=NO build

Tests

The KleponSnapshotTests target contains both logic tests (Swift Testing @Test) and snapshot tests (PointFree SnapshotTesting). Run all tests via the Klepon scheme:

xcodebuild test \
  -project Klepon.xcodeproj \
  -scheme Klepon \
  -destination 'platform=iOS Simulator,OS=26.2,name=iPhone 17' \
  -only-testing:KleponSnapshotTests \
  CODE_SIGNING_ALLOWED=NO

Validate bundled JSON content (runs on Linux in CI):

python3 Scripts/validate_content.py

Architecture

Dependency graph

AppState (injected as @EnvironmentObject) owns all services and stores:

AppState
├── ContentRepository   — loads guide_entries.json + collections.json at startup
├── SearchService       — token-based search over ContentRepository
├── FavoritesStore      — UserDefaults-backed Set<String> of entry IDs
├── RecentSearchStore   — persisted recent queries
├── RecentlyViewedStore — persisted recently viewed entry IDs
├── OndeGuideEngine     — wraps the Onde Rust/UniFFI on-device inference runtime
├── GuideAnswerService  — composes ContentRepository + GuideAnswering to answer questions
└── AccountStore        — Onde Cloud session management

On-device AI

OndeGuideEngine conforms to GuideAnswering and lazily initializes EdAgent (from the Ed Swift package) only when inference is actually requested. GuideAnswerService depends on the GuideAnswering protocol, not the concrete engine, so tests can inject StubGuideEngine without touching the Rust/UniFFI runtime.

tvOS inference is gated on hardware: Apple TV 4K 3rd generation (AppleTV14,1, A15) or newer — checked via sysctlbyname("hw.machine", ...) in OndeGuideEngine.swift.

Platform adaptations

  • iOS compact (iPhone) and tvOS: TabView-based navigation
  • iOS regular (iPad): NavigationSplitView with a sidebar
  • macOS / visionOS: same SwiftUI views as iOS, with minimum window size constraints
  • tvOS root: entirely UIKit — AppDelegate → SceneDelegate → KleponTabBarController hosting SwiftUI views via UIHostingController

Design system

KleponColor and KleponTypography define all colors and type styles. Use these rather than system defaults to keep visual consistency across platforms.

Credentials setup

Copy Configs/Secrets.local.xcconfig.example to Configs/Secrets.local.xcconfig (git-ignored) and fill in per-SDK Onde Cloud credentials (ONDE_APP_ID, ONDE_APP_SECRET). Without this file the private guide feature shows a configuration error at runtime but the rest of the app works.

For testing on a physical device over LAN, copy Configs/Debug.local.xcconfig.example to Configs/Debug.local.xcconfig and set ONDE_WEB_DEV_HOST to your Mac's LAN IP (ipconfig getifaddr en0).