PlantGuide Feature Roadmap
Implementation plan for 8 new features: Dark Mode, CloudKit Sync, Plant Rooms, Animations, Flexible Snoozing, Today View, Batch Actions, and Progress Photos.
Requirements Summary
| Feature |
Specification |
| Dark Mode |
System-following + manual toggle, new color token system |
| CloudKit |
NSPersistentCloudKitContainer, fresh installs only |
| Rooms |
User-creatable Room entity, defaults: Kitchen, Living Room, Bedroom, Bathroom, Office, Patio/Balcony, Other |
| Snoozing |
3 days, 1 week, 2 weeks, 1 month options |
| Today View |
Replaces Care tab, shows overdue + today's tasks |
| Batch Actions |
"Water all" for room or selection, logs per-plant |
| Progress Photos |
Scheduled reminders, time-lapse with user-controllable speed slider |
| Animations |
Subtle, defining—specified per context below |
Tab structure after implementation:
Camera | Browse | Collection | Today | Settings
Implementation Order
| Phase |
Feature |
Effort |
Notes |
| 1 |
Dark Mode + Color Tokens |
S |
Foundation for all UI |
| 2 |
CloudKit Sync |
M |
Must happen before new entities to avoid migration pain |
| 3 |
Plant Rooms/Zones |
M |
New Room entity (synced), user-creatable with defaults |
| 4 |
Subtle Animations |
M |
Design system completion |
| 5 |
Flexible Snoozing |
S |
Extend existing CareTask system |
| 6 |
Today View |
M |
Replace Care tab |
| 7 |
Batch Actions |
S |
Builds on rooms |
| 8 |
Progress Photos |
L |
New entity, reminders, time-lapse playback |
Dependency Graph
Phase 1: Dark Mode + Color Tokens
Technical Approach
Create a centralized design system with semantic color tokens that respond to color scheme.
Files to Create
| File |
Purpose |
Core/DesignSystem/ColorTokens.swift |
Semantic colors (background, surface, textPrimary, textSecondary, accent, destructive, etc.) |
Core/DesignSystem/AppearanceManager.swift |
@Observable class managing appearance preference (system/light/dark) |
Core/DesignSystem/DesignSystem.swift |
Namespace for tokens + spacing + typography |
Files to Modify
| File |
Change |
PlantGuideApp.swift |
Inject AppearanceManager, apply .preferredColorScheme() |
MainTabView.swift |
Replace .tint(.green) with DesignSystem.Colors.accent |
SettingsView.swift |
Add appearance picker (System/Light/Dark) |
| All Views |
Replace hardcoded colors with semantic tokens |
Color Token Structure
Risks & Mitigations
| Risk |
Mitigation |
| Inconsistent adoption |
Grep for hardcoded Color( and .foregroundColor after implementation |
| Asset catalog bloat |
Use programmatic colors with UIColor { traitCollection in } |
Tests
- Unit:
AppearanceManager persists preference correctly
- UI: Screenshot tests for light/dark variants of key screens
Phase 2: CloudKit Sync
Technical Approach
Replace NSPersistentContainer with NSPersistentCloudKitContainer. Enable automatic sync.
Files to Modify
| File |
Change |
CoreDataStack.swift |
Switch to NSPersistentCloudKitContainer, configure CloudKit container |
PlantGuideModel.xcdatamodeld |
Enable "Used with CloudKit" on configuration |
PlantGuide.entitlements |
Add CloudKit entitlement + iCloud container |
Info.plist |
Add NSUbiquitousContainerIdentifier if needed |
Xcode Configuration
- Enable CloudKit capability in Signing & Capabilities
- Create CloudKit container:
iCloud.com.yourteam.PlantGuide
- Enable "CloudKit" checkbox on Core Data model configuration
Schema Considerations
- All entities must have no unique constraints (CloudKit limitation)
- All relationships must have inverses
- No
Undefined attribute types
- Optional attributes for any field that might be nil during sync
Risks & Mitigations
| Risk |
Mitigation |
| Sync conflicts |
CloudKit uses last-writer-wins; acceptable for this app |
| Large photos slow sync |
Store photos in CKAsset (handled automatically by Core Data) |
| Offline-first gaps |
NSPersistentCloudKitContainer handles this automatically |
| Rate limiting |
Monitor CloudKit dashboard; unlikely for personal use |
Tests
- Integration: Create plant on device A, verify appears on device B
- Unit: Mock
NSPersistentCloudKitContainer for offline behavior
Phase 3: Plant Rooms/Zones
Technical Approach
New Room entity with one-to-many relationship to Plant. Replace PlantLocation enum usage.
Files to Create
| File |
Purpose |
Domain/Entities/Room.swift |
Room struct (id, name, icon, sortOrder, isDefault) |
Domain/UseCases/Room/CreateDefaultRoomsUseCase.swift |
Creates 7 default rooms on first launch |
Domain/UseCases/Room/ManageRoomsUseCase.swift |
CRUD for rooms |
Domain/RepositoryInterfaces/RoomRepositoryProtocol.swift |
Repository protocol |
Data/Repositories/CoreDataRoomRepository.swift |
Core Data implementation |
Presentation/Scenes/Rooms/RoomsListView.swift |
Settings screen to manage rooms |
Presentation/Scenes/Rooms/RoomEditorView.swift |
Create/edit room |
Presentation/Scenes/Rooms/RoomsViewModel.swift |
ViewModel |
Files to Modify
| File |
Change |
PlantGuideModel.xcdatamodeld |
Add RoomMO entity, relationship to PlantMO |
Plant.swift |
Change location: PlantLocation? to roomID: UUID? |
PlantMO |
Add room relationship |
CollectionView.swift |
Add room filter/grouping option |
PlantDetailView.swift |
Room picker instead of location enum |
SettingsView.swift |
Add "Manage Rooms" row |
DIContainer.swift |
Register room repository and use cases |
Default Rooms
Risks & Mitigations
| Risk |
Mitigation |
| Orphaned plants when room deleted |
Cascade to "Other" room or prompt user |
Migration from PlantLocation enum |
Map existing enum values to default rooms in migration |
Tests
- Unit:
CreateDefaultRoomsUseCase creates exactly 7 rooms
- Unit: Room deletion cascades plants correctly
- UI: Room picker appears in plant detail
Phase 4: Subtle Animations
Technical Approach
Create reusable animation modifiers and view extensions. Apply consistently.
Files to Create
| File |
Purpose |
Core/DesignSystem/Animations.swift |
Standard timing curves, durations |
Presentation/Common/Modifiers/FadeInModifier.swift |
Staggered fade-in for lists |
Presentation/Common/Modifiers/TaskCompleteModifier.swift |
Checkmark + confetti burst |
Presentation/Common/Modifiers/WateringFeedbackModifier.swift |
Water droplet animation |
Presentation/Common/Modifiers/PlantGrowthModifier.swift |
Subtle scale pulse |
Presentation/Common/Components/SuccessCheckmark.swift |
Animated checkmark component |
Animation Specifications
| Context |
Animation |
| Task complete |
Checkmark draws in (0.3s), row scales down + fades (0.2s), optional confetti particles |
| Watering feedback |
3 water droplets fall and fade (0.5s staggered) |
| List appearance |
Staggered fade-in, 0.05s delay per item, max 10 items animated |
| Tab switch |
Cross-fade (0.2s) |
| Card press |
Scale to 0.97 on press, spring back |
| Pull to refresh |
Plant icon rotates while loading |
| Empty state |
Gentle float animation on illustration |
Timing Standards
Files to Modify
| File |
Change |
CareTaskRow.swift |
Add completion animation |
CollectionView.swift |
Staggered list animation |
MainTabView.swift |
Tab transition |
| All cards/buttons |
Press feedback |
Risks & Mitigations
| Risk |
Mitigation |
| Animation jank on older devices |
Use Animation.interactiveSpring which degrades gracefully |
| Overdone animations |
Keep durations under 0.5s, use subtle scales (0.95-1.05 range) |
| Accessibility |
Respect UIAccessibility.isReduceMotionEnabled |
Tests
- Unit: Animations respect reduce motion setting
- Manual: Test on oldest supported device (iPhone XR or similar)
Phase 5: Flexible Snoozing
Technical Approach
Extend existing snooze system with new intervals. Add snooze picker UI.
Files to Create
| File |
Purpose |
Presentation/Common/Components/SnoozePickerView.swift |
Bottom sheet with snooze options |
Files to Modify
| File |
Change |
CareTask.swift |
Add snoozedUntil: Date? property, snoozeCount: Int |
CareTaskRow.swift |
Replace swipe actions with snooze picker trigger |
CareScheduleViewModel.swift |
Update snoozeTask() to accept SnoozeInterval enum |
NotificationService.swift |
Reschedule notification on snooze |
CareTaskMO |
Add snoozedUntil and snoozeCount attributes |
Snooze Options
UI Behavior
- Swipe left on task → shows snooze picker (bottom sheet)
- Picker shows 4 options + "Custom date" option
- Snoozed tasks show "Snoozed until [date]" badge
- Snoozed tasks appear in "Snoozed" filter section
Risks & Mitigations
| Risk |
Mitigation |
| Users abuse snooze |
Track snoozeCount, optionally show "frequently snoozed" indicator |
| Notification sync |
Cancel old notification, schedule new one in same transaction |
Tests
- Unit:
snoozeTask(interval:) calculates correct future date
- Unit: Snooze count increments
- UI: Snoozed task appears in correct section
Phase 6: Today View
Technical Approach
New view replacing Care tab. Dashboard with overdue + today sections, grouped by room.
Files to Create
| File |
Purpose |
Presentation/Scenes/TodayView/TodayView.swift |
Main dashboard |
Presentation/Scenes/TodayView/TodayViewModel.swift |
ViewModel |
Presentation/Scenes/TodayView/Components/TaskSection.swift |
Overdue/Today section |
Presentation/Scenes/TodayView/Components/RoomTaskGroup.swift |
Tasks grouped by room |
Presentation/Scenes/TodayView/Components/QuickStatsBar.swift |
Today's progress (3/7 tasks done) |
Files to Modify
| File |
Change |
MainTabView.swift |
Replace Care tab with Today tab |
DIContainer.swift |
Add makeTodayViewModel() |
Today View Layout
Risks & Mitigations
| Risk |
Mitigation |
| Empty state confusion |
Show encouraging empty state: "All caught up!" |
| Performance with many tasks |
Limit visible tasks, add "Show all" expansion |
Tests
- Unit: Tasks correctly grouped by overdue/today/room
- UI: Empty state displays correctly
- UI: Task completion updates stats bar
Phase 7: Batch Actions
Technical Approach
Multi-select mode + batch action bar. Actions apply to selection or entire room.
Files to Create
| File |
Purpose |
Presentation/Common/Components/BatchActionBar.swift |
Floating action bar |
Domain/UseCases/PlantCare/BatchCompleteTasksUseCase.swift |
Complete multiple tasks |
Files to Modify
| File |
Change |
TodayView.swift |
Add edit mode, selection state |
TodayViewModel.swift |
selectedTaskIDs: Set<UUID>, batch action methods |
RoomTaskGroup.swift |
"Water all in [Room]" button |
CareScheduleRepositoryProtocol.swift |
Add batchUpdate(taskIDs:completion:) |
UI Behavior
- Room-level batch: Each room group has "Water all" button (only for watering tasks)
- Selection mode: Long-press or Edit button enables multi-select
- Batch bar: Appears at bottom when items selected: "Complete (5)" button
- Logging: Each plant gets individual
CareTask completion entry
Batch Action Bar
Risks & Mitigations
| Risk |
Mitigation |
| Accidental batch complete |
Require confirmation for >3 tasks |
| Mixed task types in selection |
Only show "Complete" (generic), not task-specific action |
Tests
- Unit:
BatchCompleteTasksUseCase creates individual completion records
- UI: Selection count updates correctly
- UI: Confirmation appears for large batches
Phase 8: Progress Photos
Technical Approach
New ProgressPhoto entity linked to Plant. Scheduled reminders for capture. Time-lapse viewer with speed control.
Files to Create
| File |
Purpose |
Domain/Entities/ProgressPhoto.swift |
Photo entity (id, plantID, imageData, dateTaken, notes) |
Domain/RepositoryInterfaces/ProgressPhotoRepositoryProtocol.swift |
Repository protocol |
Data/Repositories/CoreDataProgressPhotoRepository.swift |
Core Data implementation |
Domain/UseCases/Photos/CaptureProgressPhotoUseCase.swift |
Save photo with metadata |
Domain/UseCases/Photos/SchedulePhotoReminderUseCase.swift |
Weekly/monthly reminder |
Presentation/Scenes/ProgressPhotos/ProgressPhotoGalleryView.swift |
Grid of photos for plant |
Presentation/Scenes/ProgressPhotos/ProgressPhotoCaptureView.swift |
Camera capture screen |
Presentation/Scenes/ProgressPhotos/TimeLapsePlayerView.swift |
Time-lapse viewer |
Presentation/Scenes/ProgressPhotos/ProgressPhotosViewModel.swift |
ViewModel |
Files to Modify
| File |
Change |
PlantGuideModel.xcdatamodeld |
Add ProgressPhotoMO entity |
PlantDetailView.swift |
Add "Progress Photos" section |
NotificationService.swift |
Add photo reminder notification type |
DIContainer.swift |
Register photo repositories and use cases |
Core Data Entity
Time-Lapse Player
Photo Reminder Options
- Weekly (same day each week)
- Bi-weekly
- Monthly (same date each month)
- Off
Storage Strategy
- Full resolution stored as
Binary Data with "Allows External Storage" enabled
- Thumbnail (200x200) stored inline for fast gallery loading
- CloudKit syncs via
CKAsset automatically
Risks & Mitigations
| Risk |
Mitigation |
| Storage bloat |
Compress to HEIC, limit resolution to 2048px max dimension |
| CloudKit photo sync slow |
Show sync indicator, allow offline viewing of local photos |
| Gallery performance |
Load thumbnails only, full image on tap |
Tests
- Unit: Photo saved with correct metadata
- Unit: Thumbnail generated at correct size
- UI: Time-lapse plays at correct speed
- Integration: Photo syncs to CloudKit
Red Team Check
Potential Issues Across All Phases
| Issue |
Phase |
Severity |
Mitigation |
| CloudKit container misconfigured |
2 |
Critical |
Test on real device early, not just simulator |
| Color tokens not adopted everywhere |
1 |
Medium |
Add SwiftLint rule or grep check |
| Room deletion orphans data |
3 |
High |
Cascade to "Other" room, never delete "Other" |
| Animation performance on old devices |
4 |
Medium |
Test on iPhone XR/XS, use interactiveSpring |
| Snooze notifications don't update |
5 |
High |
Wrap in transaction: cancel + reschedule |
| Today view empty state confusing |
6 |
Low |
Design clear empty states early |
| Batch action logging wrong |
7 |
Medium |
Unit test verifies individual records |
| Photo storage exceeds iCloud quota |
8 |
Medium |
Warn user when approaching limit |
Rollback Strategy
Each phase is independently deployable. If issues arise:
- Feature flags: Wrap new features in
@AppStorage("feature_X_enabled") flags
- Database migrations: All Core Data changes are additive (new entities/attributes), never destructive
- CloudKit schema: Once deployed to production, schema can only be extended, not modified
Totals
| Phase |
Feature |
New Files |
Modified Files |
Effort |
| 1 |
Dark Mode |
3 |
~15 |
S |
| 2 |
CloudKit |
0 |
4 |
M |
| 3 |
Rooms |
8 |
7 |
M |
| 4 |
Animations |
6 |
5 |
M |
| 5 |
Snoozing |
1 |
5 |
S |
| 6 |
Today View |
5 |
2 |
M |
| 7 |
Batch Actions |
2 |
4 |
S |
| 8 |
Progress Photos |
9 |
4 |
L |
Total: ~34 new files, ~30 modified files