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>
128 lines
3.1 KiB
Swift
128 lines
3.1 KiB
Swift
//
|
|
// MoodEntryModel.swift
|
|
// Reflect
|
|
//
|
|
// SwiftData model replacing Core Data MoodEntry
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftData
|
|
|
|
// MARK: - Entry Type Enum
|
|
|
|
enum EntryType: Int, Codable {
|
|
case listView = 0
|
|
case widget = 1
|
|
case watch = 2
|
|
case shortcut = 3
|
|
case filledInMissing = 4
|
|
case notification = 5
|
|
case header = 6
|
|
case siri = 7
|
|
case controlCenter = 8
|
|
case liveActivity = 9
|
|
}
|
|
|
|
// MARK: - SwiftData Model
|
|
|
|
@Model
|
|
final class MoodEntryModel {
|
|
// Primary attributes - CloudKit requires default values
|
|
var forDate: Date = Date()
|
|
var moodValue: Int = 0
|
|
var timestamp: Date = Date()
|
|
var weekDay: Int = 1
|
|
var entryType: Int = 0
|
|
|
|
// Metadata - CloudKit requires default values
|
|
var canEdit: Bool = true
|
|
var canDelete: Bool = true
|
|
|
|
// Journal & Media (NEW)
|
|
var notes: String?
|
|
var photoID: UUID?
|
|
|
|
// Weather
|
|
var weatherJSON: String?
|
|
|
|
// Guided Reflection
|
|
var reflectionJSON: String?
|
|
|
|
// AI-extracted theme tags (JSON array of strings)
|
|
var tagsJSON: String?
|
|
|
|
// Computed properties
|
|
var mood: Mood {
|
|
Mood(rawValue: moodValue) ?? .missing
|
|
}
|
|
|
|
/// Decoded tags from tagsJSON, or empty array if none
|
|
var tags: [String] {
|
|
guard let json = tagsJSON, let data = json.data(using: .utf8),
|
|
let decoded = try? JSONDecoder().decode([String].self, from: data) else {
|
|
return []
|
|
}
|
|
return decoded
|
|
}
|
|
|
|
/// Whether this entry has AI-extracted tags
|
|
var hasTags: Bool {
|
|
!tags.isEmpty
|
|
}
|
|
|
|
var moodString: String {
|
|
mood.strValue
|
|
}
|
|
|
|
init(
|
|
forDate: Date,
|
|
mood: Mood,
|
|
entryType: EntryType,
|
|
canEdit: Bool = true,
|
|
canDelete: Bool = true,
|
|
notes: String? = nil,
|
|
photoID: UUID? = nil,
|
|
weatherJSON: String? = nil,
|
|
reflectionJSON: String? = nil
|
|
) {
|
|
self.forDate = forDate
|
|
self.moodValue = mood.rawValue
|
|
self.timestamp = Date()
|
|
self.weekDay = Calendar.current.component(.weekday, from: forDate)
|
|
self.entryType = entryType.rawValue
|
|
self.canEdit = canEdit
|
|
self.canDelete = canDelete
|
|
self.notes = notes
|
|
self.photoID = photoID
|
|
self.weatherJSON = weatherJSON
|
|
self.reflectionJSON = reflectionJSON
|
|
}
|
|
|
|
// Convenience initializer for raw values
|
|
init(
|
|
forDate: Date,
|
|
moodValue: Int,
|
|
entryType: Int,
|
|
timestamp: Date = Date(),
|
|
weekDay: Int? = nil,
|
|
canEdit: Bool = true,
|
|
canDelete: Bool = true,
|
|
notes: String? = nil,
|
|
photoID: UUID? = nil,
|
|
weatherJSON: String? = nil,
|
|
reflectionJSON: String? = nil
|
|
) {
|
|
self.forDate = forDate
|
|
self.moodValue = moodValue
|
|
self.timestamp = timestamp
|
|
self.weekDay = weekDay ?? Calendar.current.component(.weekday, from: forDate)
|
|
self.entryType = entryType
|
|
self.canEdit = canEdit
|
|
self.canDelete = canDelete
|
|
self.notes = notes
|
|
self.photoID = photoID
|
|
self.weatherJSON = weatherJSON
|
|
self.reflectionJSON = reflectionJSON
|
|
}
|
|
}
|