Files
PlantGuide/CLAUDE.md
Trey t 1ae9c884c8 Rebuild UI test foundation with page objects, wait helpers, and screen objects
Replace brittle localized-string selectors and broken wait helpers with a
robust, identifier-first UI test infrastructure. All 41 UI tests pass on
iOS 26.2 simulator (iPhone 17).

Foundation:
- BaseUITestCase with deterministic launch helpers (launchClean, launchOffline)
- WaitHelpers (waitUntilHittable, waitUntilGone, tapWhenReady) replacing sleep()
- UITestID enum mirroring AccessibilityIdentifiers from the app target
- Screen objects: TabBarScreen, CameraScreen, CollectionScreen, TodayScreen,
  SettingsScreen, PlantDetailScreen

Key fixes:
- Tab navigation uses waitForExistence+tap instead of isHittable (unreliable
  in iOS 26 simulator)
- Tests handle real app state (empty collection, no camera permission)
- Increased timeouts for parallel clone execution
- Added NetworkMonitorProtocol and protocol-typed DI for testability
- Fixed actor-isolation issues in unit test mocks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:36:54 -06:00

7.1 KiB

CLAUDE.md

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

Build & Test Commands

# Build
xcodebuild -project PlantGuide.xcodeproj -scheme PlantGuide -configuration Debug

# Run all tests
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17'

# Run a single test class
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:PlantGuideTests/HybridIdentificationUseCaseTests

# Run a single test method
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:PlantGuideTests/SavePlantUseCaseTests/testSavePlant_Success

# Build UI tests only (compile check, no simulator required)
xcodebuild build-for-testing -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -quiet

# Run all UI tests
xcodebuild test -project PlantGuide.xcodeproj -scheme PlantGuide -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:PlantGuideUITests

App Structure

Tab Navigation: Camera | Collection | Today | Settings

Implemented Features

  • Plant Identification - Camera-based plant ID using on-device ML + PlantNet API fallback
  • Plant Collection - Save identified plants with care schedules
  • Plant Rooms/Zones - Organize plants by room (Kitchen, Living Room, Bedroom, etc.)
  • Today View - Dashboard showing overdue + today's care tasks grouped by room
  • Progress Photos - Capture growth photos with time-lapse playback and photo reminders
  • Care Scheduling - Watering, fertilizing, repotting, pruning, pest control tasks
  • Notifications - Local notifications for care reminders and photo reminders
  • CloudKit Sync - iCloud sync via NSPersistentCloudKitContainer
  • Dark Mode - System-following color scheme with semantic color tokens

Architecture

Clean Architecture + MVVM with three main layers:

Presentation (SwiftUI Views + ViewModels)
    ↓
Domain (Use Cases + Entities + Repository Protocols)
    ↓
Data (Repository Implementations + Data Sources)

Key Patterns

  • Dependency Injection: DIContainer (singleton) provides all dependencies via LazyService<T> wrappers
  • Use Cases: Each user action is a separate use case class (e.g., SavePlantUseCase, HybridIdentificationUseCase)
  • Repository Pattern: Protocols in Domain layer, implementations in Data layer
  • Actor-based Concurrency: PlantClassificationService, NotificationService, and Core Data background tasks use actors
  • ViewModels: @MainActor @Observable classes, created via DIContainer.makeXxxViewModel()
  • Core Data + CloudKit: NSPersistentCloudKitContainer with async store loading and waitForStoreLoaded() pattern

Data Flow Example

CameraView → IdentificationViewModel → HybridIdentificationUseCase
    → PlantClassificationService (on-device ML)
    → PlantNetAPIService (online fallback)
    → IdentificationView (results)

Directory Structure

  • App/ - Entry point, configuration, API keys
  • Core/DI/ - DIContainer and LazyService
  • Core/DesignSystem/ - Color tokens, animations, appearance management
  • Core/Services/ - NotificationService
  • Domain/ - Business logic: Entities, UseCases, RepositoryInterfaces
  • Data/ - Persistence: CoreData, Repositories, API services, Mappers
  • ML/ - Core ML inference service and image preprocessing
  • Presentation/ - SwiftUI views, ViewModels, navigation
    • Scenes/ - Feature-specific views (Camera, Collection, Today, Settings, ProgressPhotos, Rooms)
    • Common/ - Shared components and modifiers

External Services

  • PlantNet API (my.plantnet.org) - Plant identification via image upload (500 free/day)
  • Trefle API (trefle.io) - Botanical care data (400K+ species)
  • CloudKit - iCloud sync for plants, rooms, care tasks, and progress photos

API keys configured in App/Configuration/APIKeys.swift via environment variables.

ML Model

PlantNet-300K ResNet50 for on-device plant classification:

  • Input: 224x224 RGB image with ImageNet normalization
  • Output: 1,081 plant species probabilities
  • Conversion scripts in scripts/ directory

Core Data Model

Entities (all CloudKit-compatible with optional attributes):

  • PlantMO - Plant records with identification data
  • RoomMO - User-created rooms/zones
  • CareScheduleMO - Care schedule configuration per plant
  • CareTaskMO - Individual care tasks (watering, fertilizing, etc.)
  • ProgressPhotoMO - Progress photos with thumbnails
  • IdentificationMO - Plant identification history
  • PlantCareInfoMO - Cached care information from APIs

Testing

  • Unit tests in PlantGuideTests/ with mock implementations in Mocks/
  • Test fixtures available for Plant, CareTask, PlantCareSchedule
  • Mock services: MockPlantCollectionRepository, MockNetworkService, etc.
  • In-memory Core Data stack for test isolation: CoreDataStack(inMemory: true)

UI Testing

UI tests live in PlantGuideUITests/ using a page-object foundation. See Docs/XCUITest-Authoring.md for the full guide.

Key patterns:

  • Inherit BaseUITestCase, not XCTestCase
  • Launch with launchClean(), launchWithMockData(), or launchOffline()
  • Locate elements via UITestID.* identifiers (mirrors AccessibilityIdentifiers in app)
  • Navigate with screen objects: TabBarScreen, CameraScreen, CollectionScreen, TodayScreen, SettingsScreen, PlantDetailScreen
  • Wait with waitForExistence(timeout:), waitUntilHittable(), waitUntilGone() -- never sleep()
  • One assertion focus per test method

Claude GitHub App

Installation

  1. Go to github.com/apps/claude
  2. Click "Install" and select this repository
  3. Grant the requested permissions (read/write for code, issues, and pull requests)
  4. Authenticate with your Anthropic account when prompted

How It Works

  • The app reads this CLAUDE.md file for project context and contribution rules
  • Claude responds to @claude mentions in issues and PRs
  • No API key configuration needed - authentication is handled through the GitHub App integration

Triggering Claude

  • Issues: Mention @claude in an issue to request implementation help
  • PRs: Mention @claude to request code review or changes

Claude Contribution Rules

Scope

  • Work ONLY on the issue explicitly assigned to you.
  • One issue = one pull request.
  • Do not refactor unrelated code.
  • Do not change public APIs unless the issue explicitly says so.

Safety Rules

  • Never auto-merge.
  • Never force-push to main.
  • Never delete code unless instructed.
  • Preserve existing behavior unless tests say otherwise.

iOS Rules

  • Respect Swift concurrency (MainActor, async/await).
  • Do not introduce Combine unless already used.
  • Prefer pure functions for new logic.
  • No new dependencies without approval.

Output Expectations

Each PR must include:

  • Clear summary of changes
  • Files touched (with rationale)
  • Risks and how to test