Files
PlantGuide/Docs/IMPLEMENTATION_PLAN.md
Trey t 136dfbae33 Add PlantGuide iOS app with plant identification and care management
- Implement camera capture and plant identification workflow
- Add Core Data persistence for plants, care schedules, and cached API data
- Create collection view with grid/list layouts and filtering
- Build plant detail views with care information display
- Integrate Trefle botanical API for plant care data
- Add local image storage for captured plant photos
- Implement dependency injection container for testability
- Include accessibility support throughout the app

Bug fixes in this commit:
- Fix Trefle API decoding by removing duplicate CodingKeys
- Fix LocalCachedImage to load from correct PlantImages directory
- Set dateAdded when saving plants for proper collection sorting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 12:18:01 -06:00

328 lines
10 KiB
Markdown

# Botanica - Plant Identification iOS App
## Overview
Custom iOS 17+ app using SwiftUI that identifies plants via camera with care schedules.
**Stack:**
- On-device ML: PlantNet-300K model converted to Core ML
- Online API: Pl@ntNet (my.plantnet.org) for higher accuracy
- Care data: Trefle API (open source botanical database)
- Architecture: Clean Architecture + MVVM
---
## Phase 1: Foundation (Week 1-2)
**Goal:** Core infrastructure with camera capture
| Task | Description |
|------|-------------|
| 1.1 | Create Xcode project (iOS 17+, SwiftUI) |
| 1.2 | Set up folder structure (App/, Core/, Domain/, Data/, ML/, Presentation/) |
| 1.3 | Implement `DIContainer.swift` for dependency injection |
| 1.4 | Create domain entities: `Plant`, `PlantIdentification`, `PlantCareSchedule`, `CareTask` |
| 1.5 | Define repository protocols in `Domain/RepositoryInterfaces/` |
| 1.6 | Build `NetworkService.swift` with async/await and multipart upload |
| 1.7 | Implement `CameraView` + `CameraViewModel` with AVFoundation |
| 1.8 | Set up Core Data stack for persistence |
| 1.9 | Create tab navigation (Camera, Collection, Care, Settings) |
**Deliverable:** Working camera capture with photo preview
---
## Phase 2: On-Device ML (Week 3-4)
**Goal:** Offline plant identification with Core ML
| Task | Description |
|------|-------------|
| 2.1 | Download PlantNet-300K pre-trained ResNet weights |
| 2.2 | Convert to Core ML using `coremltools` (Python script) |
| 2.3 | Add `PlantNet300K.mlpackage` to Xcode |
| 2.4 | Create `PlantLabels.json` with 1,081 species names |
| 2.5 | Implement `PlantClassificationService.swift` using Vision framework |
| 2.6 | Create `ImagePreprocessor.swift` for model input normalization |
| 2.7 | Build `IdentifyPlantOnDeviceUseCase.swift` |
| 2.8 | Create `IdentificationView` showing results with confidence scores |
| 2.9 | Build `SpeciesMatchCard` and `ConfidenceIndicator` components |
| 2.10 | Performance test on device (target: <500ms inference) |
**Deliverable:** End-to-end offline identification flow
---
## Phase 3: PlantNet API Integration (Week 5-6)
**Goal:** Hybrid identification with API fallback
| Task | Description |
|------|-------------|
| 3.1 | Register at my.plantnet.org for API key |
| 3.2 | Create `PlantNetEndpoints.swift` (POST /v2/identify/{project}) |
| 3.3 | Implement `PlantNetAPIService.swift` with multipart image upload |
| 3.4 | Create DTOs: `PlantNetIdentifyResponseDTO`, `PlantNetSpeciesDTO` |
| 3.5 | Build `PlantNetMapper.swift` (DTO → Domain entity) |
| 3.6 | Implement `IdentifyPlantOnlineUseCase.swift` |
| 3.7 | Create `HybridIdentificationUseCase.swift` (on-device first, API for confirmation) |
| 3.8 | Add network reachability monitoring |
| 3.9 | Handle rate limiting (500 free requests/day) |
| 3.10 | Implement `IdentificationCache.swift` for previous results |
**Deliverable:** Hybrid identification combining on-device + API
---
## Phase 4: Trefle API & Plant Care (Week 7-8)
**Goal:** Complete care information and scheduling
| Task | Description |
|------|-------------|
| 4.1 | Register at trefle.io for API token |
| 4.2 | Create `TrefleEndpoints.swift` (GET /plants/search, GET /species/{slug}) |
| 4.3 | Implement `TrefleAPIService.swift` |
| 4.4 | Create DTOs: `TrefleSpeciesDTO`, `GrowthDataDTO` |
| 4.5 | Build `TrefleMapper.swift` mapping growth data to care schedules |
| 4.6 | Implement `FetchPlantCareUseCase.swift` |
| 4.7 | Create `CreateCareScheduleUseCase.swift` |
| 4.8 | Build `PlantDetailView` with `CareInformationSection` |
| 4.9 | Implement `CareScheduleView` with upcoming tasks |
| 4.10 | Add local notifications for care reminders |
**Deliverable:** Full plant care data with watering/fertilizer schedules
---
## Phase 5: Plant Collection & Persistence (Week 9-10)
**Goal:** Saved plants with full offline support
| Task | Description |
|------|-------------|
| 5.1 | Define Core Data models (PlantMO, CareScheduleMO, IdentificationMO) |
| 5.2 | Implement `CoreDataPlantStorage.swift` |
| 5.3 | Build `PlantCollectionRepository.swift` |
| 5.4 | Create use cases: `SavePlantUseCase`, `FetchCollectionUseCase` |
| 5.5 | Build `CollectionView` with grid layout |
| 5.6 | Implement `ImageCache.swift` for offline images |
| 5.7 | Add search/filter in collection |
**Deliverable:** Full plant collection management with offline support
---
## Phase 6: Polish & Release (Week 11-12)
**Goal:** Production-ready application
| Task | Description |
|------|-------------|
| 6.1 | Build `SettingsView` (offline mode toggle, API status, cache clear) |
| 6.2 | Add comprehensive error handling with `ErrorView` |
| 6.3 | Implement loading states with shimmer effects |
| 6.4 | Add accessibility labels and Dynamic Type support |
| 6.5 | Performance optimization pass |
| 6.6 | Write unit tests for use cases and services |
| 6.7 | Write UI tests for critical flows |
| 6.8 | Final QA and bug fixes |
**Deliverable:** App Store ready application
---
## Project Structure
```
Botanica/
├── App/
│ ├── BotanicaApp.swift
│ └── Configuration/
│ ├── AppConfiguration.swift
│ └── APIKeys.swift
├── Core/
│ ├── DI/DIContainer.swift
│ ├── Extensions/
│ └── Utilities/
├── Domain/
│ ├── Entities/
│ │ ├── Plant.swift
│ │ ├── PlantIdentification.swift
│ │ ├── PlantCareSchedule.swift
│ │ └── CareTask.swift
│ ├── UseCases/
│ │ ├── Identification/
│ │ │ ├── IdentifyPlantOnDeviceUseCase.swift
│ │ │ ├── IdentifyPlantOnlineUseCase.swift
│ │ │ └── HybridIdentificationUseCase.swift
│ │ ├── PlantCare/
│ │ │ ├── FetchPlantCareUseCase.swift
│ │ │ └── CreateCareScheduleUseCase.swift
│ │ └── Collection/
│ └── RepositoryInterfaces/
├── Data/
│ ├── Repositories/
│ ├── DataSources/
│ │ ├── Remote/
│ │ │ ├── PlantNetAPI/
│ │ │ │ ├── PlantNetAPIService.swift
│ │ │ │ └── DTOs/
│ │ │ ├── TrefleAPI/
│ │ │ │ ├── TrefleAPIService.swift
│ │ │ │ └── DTOs/
│ │ │ └── NetworkService/
│ │ └── Local/
│ │ ├── CoreData/
│ │ └── Cache/
│ └── Mappers/
├── ML/
│ ├── Models/
│ │ └── PlantNet300K.mlpackage
│ ├── Services/
│ │ └── PlantClassificationService.swift
│ └── Preprocessing/
├── Presentation/
│ ├── Scenes/
│ │ ├── Camera/
│ │ ├── Identification/
│ │ ├── PlantDetail/
│ │ ├── Collection/
│ │ ├── CareSchedule/
│ │ └── Settings/
│ ├── Common/Components/
│ └── Navigation/
└── Resources/
├── PlantLabels.json
└── Assets.xcassets
```
---
## API Details
### Pl@ntNet API
- **Base URL:** `https://my-api.plantnet.org`
- **Endpoint:** `POST /v2/identify/{project}`
- **Free tier:** 500 requests/day
- **Coverage:** 77,565 species
- **Documentation:** [my.plantnet.org/doc](https://my.plantnet.org/doc)
### Trefle API
- **Base URL:** `https://trefle.io/api/v1`
- **Endpoints:**
- `GET /plants/search?q={name}`
- `GET /species/{slug}`
- **Data:** Light requirements, watering, soil, temperature, fertilizer, growth info
- **Documentation:** [docs.trefle.io](https://docs.trefle.io)
---
## Core ML Conversion
### Prerequisites
```bash
pip install torch torchvision coremltools pillow numpy
```
### Conversion Script
```python
# scripts/convert_plantnet_to_coreml.py
import torch
import torchvision.models as models
import coremltools as ct
# Load PlantNet-300K pre-trained ResNet
model = models.resnet50(weights=None)
model.fc = torch.nn.Linear(model.fc.in_features, 1081)
model.load_state_dict(torch.load("resnet50_weights_best_acc.tar", map_location='cpu')['state_dict'])
model.eval()
# Trace for conversion
traced = torch.jit.trace(model, torch.rand(1, 3, 224, 224))
# Convert to Core ML
image_input = ct.ImageType(
name="image",
shape=(1, 3, 224, 224),
scale=1/255.0,
bias=[-0.485/0.229, -0.456/0.224, -0.406/0.225],
color_layout=ct.colorlayout.RGB
)
mlmodel = ct.convert(
traced,
inputs=[image_input],
convert_to="mlprogram",
minimum_deployment_target=ct.target.iOS17,
compute_precision=ct.precision.FLOAT16,
)
mlmodel.save("PlantNet300K.mlpackage")
```
### Download Weights
```bash
# From Zenodo (PlantNet-300K official)
wget https://zenodo.org/records/4726653/files/resnet50_weights_best_acc.tar
```
---
## Key Data Models
### Plant Entity
```swift
struct Plant: Identifiable, Sendable {
let id: UUID
let scientificName: String
let commonNames: [String]
let family: String
let genus: String
let imageURLs: [URL]
let dateIdentified: Date
let identificationSource: IdentificationSource
enum IdentificationSource: String {
case onDevice, plantNetAPI, hybrid
}
}
```
### PlantCareSchedule Entity
```swift
struct PlantCareSchedule: Identifiable, Sendable {
let id: UUID
let plantID: UUID
let lightRequirement: LightRequirement
let wateringSchedule: WateringSchedule
let temperatureRange: TemperatureRange
let fertilizerSchedule: FertilizerSchedule?
let tasks: [CareTask]
}
```
---
## Verification Checklist
| Test | Expected Result |
|------|-----------------|
| Camera capture | Take photo → preview displays |
| On-device ML | Photo → top 10 species with confidence scores (<500ms) |
| PlantNet API | Photo → API results match/exceed on-device accuracy |
| Trefle API | Scientific name → care data (watering, light, fertilizer) |
| Save plant | Save to collection → persists after app restart |
| Offline mode | Disable network → on-device identification still works |
| Care reminders | Create schedule → notification fires at scheduled time |
---
## Resources
- [PlantNet-300K GitHub](https://github.com/plantnet/PlantNet-300K)
- [PlantNet-300K Dataset (Zenodo)](https://zenodo.org/records/4726653)
- [Pl@ntNet API Docs](https://my.plantnet.org/doc)
- [Trefle API Docs](https://docs.trefle.io)
- [Apple Core ML Tools](https://github.com/apple/coremltools)
- [Vision Framework](https://developer.apple.com/documentation/vision)