Files
Reflect/Shared/Persisence/DataControllerUPDATE.swift
Trey t ab8d8fbdc0 Add AI-powered mental wellness features: Reflection Companion, Pattern Tags, Weekly Digest
Three new Foundation Models features to deepen user engagement with mental wellness:

1. AI Reflection Companion — personalized feedback after completing guided reflections,
   referencing the user's actual words with personality-pack-adapted tone
2. Mood Pattern Tags — auto-extracts theme tags (work, family, stress, etc.) from notes
   and reflections, displayed as colored pills on entries
3. Weekly Emotional Digest — BGTask-scheduled Sunday digest with headline, summary,
   highlight, and intention; shown as card in Insights tab with notification

All features: on-device (zero cost), premium-gated, iOS 26+ with graceful degradation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:47:28 -05:00

95 lines
2.4 KiB
Swift

//
// DataControllerUPDATE.swift
// Reflect
//
// SwiftData UPDATE operations.
//
import SwiftData
import Foundation
extension DataController {
@discardableResult
func update(entryDate: Date, withMood mood: Mood) -> Bool {
guard let entry = getEntry(byDate: entryDate) else {
return false
}
entry.moodValue = mood.rawValue
entry.timestamp = Date()
saveAndRunDataListeners()
AnalyticsManager.shared.track(.moodUpdated(mood: mood.rawValue))
return true
}
// MARK: - Notes
@discardableResult
func updateNotes(forDate date: Date, notes: String?) -> Bool {
guard let entry = getEntry(byDate: date) else {
return false
}
entry.notes = notes
saveAndRunDataListeners()
AnalyticsManager.shared.track(.noteUpdated(characterCount: (notes ?? "").count))
return true
}
// MARK: - Weather
@discardableResult
func updateWeather(forDate date: Date, weatherJSON: String?) -> Bool {
guard let entry = getEntry(byDate: date) else {
return false
}
entry.weatherJSON = weatherJSON
saveAndRunDataListeners()
return true
}
// MARK: - Guided Reflection
@discardableResult
func updateReflection(forDate date: Date, reflectionJSON: String?) -> Bool {
guard let entry = getEntry(byDate: date) else { return false }
entry.reflectionJSON = reflectionJSON
saveAndRunDataListeners()
let count = reflectionJSON.flatMap { GuidedReflection.decode(from: $0) }?.answeredCount ?? 0
AnalyticsManager.shared.track(.reflectionCompleted(answeredCount: count))
return true
}
// MARK: - Tags
@discardableResult
func updateTags(forDate date: Date, tagsJSON: String?) -> Bool {
guard let entry = getEntry(byDate: date) else { return false }
entry.tagsJSON = tagsJSON
saveAndRunDataListeners()
return true
}
// MARK: - Photo
@discardableResult
func updatePhoto(forDate date: Date, photoID: UUID?) -> Bool {
guard let entry = getEntry(byDate: date) else {
return false
}
entry.photoID = photoID
saveAndRunDataListeners()
if photoID != nil {
AnalyticsManager.shared.track(.photoAdded)
} else {
AnalyticsManager.shared.track(.photoDeleted)
}
return true
}
}