Add weather feature with WeatherKit integration for mood entries

Fetch and display weather data (temp, condition, hi/lo, humidity) when
users log a mood. Weather is stored as JSON on MoodEntryModel and shown
as a card in EntryDetailView. Premium-gated with location permission
prompt. Includes BGTask retry for failed fetches and full analytics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-11 00:16:26 -05:00
parent a1340b4deb
commit 31fb2a7fe2
15 changed files with 557 additions and 3 deletions

View File

@@ -42,6 +42,9 @@ final class MoodEntryModel {
var notes: String?
var photoID: UUID?
// Weather
var weatherJSON: String?
// Computed properties
var mood: Mood {
Mood(rawValue: moodValue) ?? .missing
@@ -58,7 +61,8 @@ final class MoodEntryModel {
canEdit: Bool = true,
canDelete: Bool = true,
notes: String? = nil,
photoID: UUID? = nil
photoID: UUID? = nil,
weatherJSON: String? = nil
) {
self.forDate = forDate
self.moodValue = mood.rawValue
@@ -69,6 +73,7 @@ final class MoodEntryModel {
self.canDelete = canDelete
self.notes = notes
self.photoID = photoID
self.weatherJSON = weatherJSON
}
// Convenience initializer for raw values
@@ -81,7 +86,8 @@ final class MoodEntryModel {
canEdit: Bool = true,
canDelete: Bool = true,
notes: String? = nil,
photoID: UUID? = nil
photoID: UUID? = nil,
weatherJSON: String? = nil
) {
self.forDate = forDate
self.moodValue = moodValue
@@ -92,5 +98,6 @@ final class MoodEntryModel {
self.canDelete = canDelete
self.notes = notes
self.photoID = photoID
self.weatherJSON = weatherJSON
}
}

View File

@@ -207,6 +207,7 @@ class UserDefaultsStore {
case lockScreenStyle
case celebrationAnimation
case hapticFeedbackEnabled
case weatherEnabled
case contentViewCurrentSelectedHeaderViewBackDays
case contentViewHeaderTag

View File

@@ -0,0 +1,32 @@
//
// WeatherData.swift
// Reflect
//
// Codable weather model stored as JSON string in MoodEntryModel.
//
import Foundation
struct WeatherData: Codable {
let conditionSymbol: String // SF Symbol from WeatherKit (e.g. "cloud.sun.fill")
let condition: String // "Partly Cloudy"
let temperature: Double // Current/average in Celsius
let highTemperature: Double // Day high in Celsius
let lowTemperature: Double // Day low in Celsius
let humidity: Double // 0.01.0
let latitude: Double
let longitude: Double
let fetchedAt: Date
// MARK: - JSON Helpers
func encode() -> String? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return String(data: data, encoding: .utf8)
}
static func decode(from json: String) -> WeatherData? {
guard let data = json.data(using: .utf8) else { return nil }
return try? JSONDecoder().decode(WeatherData.self, from: data)
}
}