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>
95 lines
2.4 KiB
Swift
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
|
|
}
|
|
}
|