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>
65 lines
2.2 KiB
Swift
65 lines
2.2 KiB
Swift
//
|
|
// AIWeeklyDigest.swift
|
|
// Reflect
|
|
//
|
|
// @Generable model and storage for AI-generated weekly emotional digest.
|
|
//
|
|
|
|
import Foundation
|
|
import FoundationModels
|
|
|
|
/// AI-generated weekly mood digest
|
|
@available(iOS 26, *)
|
|
@Generable
|
|
struct AIWeeklyDigestResponse: Equatable {
|
|
@Guide(description: "An engaging headline summarizing the week's emotional arc (3-7 words)")
|
|
var headline: String
|
|
|
|
@Guide(description: "A warm 2-3 sentence summary of the week's mood patterns and notable moments")
|
|
var summary: String
|
|
|
|
@Guide(description: "The best moment or strongest positive pattern from the week (1 sentence)")
|
|
var highlight: String
|
|
|
|
@Guide(description: "A gentle, actionable intention or suggestion for the coming week (1 sentence)")
|
|
var intention: String
|
|
|
|
@Guide(description: "SF Symbol name for the digest icon (e.g., sun.max.fill, leaf.fill, heart.fill)")
|
|
var iconName: String
|
|
}
|
|
|
|
/// Storable weekly digest (Codable for UserDefaults persistence)
|
|
struct WeeklyDigest: Codable, Equatable {
|
|
let headline: String
|
|
let summary: String
|
|
let highlight: String
|
|
let intention: String
|
|
let iconName: String
|
|
let generatedAt: Date
|
|
let weekStartDate: Date
|
|
let weekEndDate: Date
|
|
|
|
var isFromCurrentWeek: Bool {
|
|
let calendar = Calendar.current
|
|
let now = Date()
|
|
let currentWeekStart = calendar.dateInterval(of: .weekOfYear, for: now)?.start ?? now
|
|
let digestWeekStart = calendar.dateInterval(of: .weekOfYear, for: weekStartDate)?.start ?? weekStartDate
|
|
return calendar.isDate(currentWeekStart, inSameDayAs: digestWeekStart) ||
|
|
calendar.isDate(digestWeekStart, inSameDayAs: calendar.date(byAdding: .weekOfYear, value: -1, to: currentWeekStart)!)
|
|
}
|
|
|
|
/// Whether the digest was dismissed by the user
|
|
static var isDismissedKey: String { "weeklyDigestDismissedDate" }
|
|
|
|
static func markDismissed() {
|
|
GroupUserDefaults.groupDefaults.set(Date(), forKey: isDismissedKey)
|
|
}
|
|
|
|
static func isDismissed(for digest: WeeklyDigest) -> Bool {
|
|
guard let dismissedDate = GroupUserDefaults.groupDefaults.object(forKey: isDismissedKey) as? Date else {
|
|
return false
|
|
}
|
|
return dismissedDate >= digest.generatedAt
|
|
}
|
|
}
|