// // DataController.swift // Feels // // SwiftData controller replacing Core Data PersistenceController. // import SwiftData import SwiftUI import os.log @MainActor final class DataController: ObservableObject { private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.tt.feels", category: "DataController") static let shared = DataController() private(set) var container: ModelContainer var modelContext: ModelContext { container.mainContext } // Listeners for data changes (keeping existing pattern) private var editedDataClosure = [() -> Void]() // Computed properties for earliest/latest entries var earliestEntry: MoodEntryModel? { var descriptor = FetchDescriptor( sortBy: [SortDescriptor(\.forDate, order: .forward)] ) descriptor.fetchLimit = 1 return try? modelContext.fetch(descriptor).first } var latestEntry: MoodEntryModel? { var descriptor = FetchDescriptor( sortBy: [SortDescriptor(\.forDate, order: .reverse)] ) descriptor.fetchLimit = 1 return try? modelContext.fetch(descriptor).first } private init() { container = SharedModelContainer.createWithFallback(useCloudKit: true) } // MARK: - Listener Management func addNewDataListener(closure: @escaping (() -> Void)) { editedDataClosure.append(closure) } func saveAndRunDataListeners() { save() for closure in editedDataClosure { closure() } } func save() { guard modelContext.hasChanges else { return } do { try modelContext.save() } catch { Self.logger.error("Failed to save context: \(error.localizedDescription)") } } /// Refresh data from disk to pick up changes made by extensions (widget/watch). /// Call this when app becomes active. func refreshFromDisk() { // SwiftData doesn't have a direct "refresh from disk" API. // We achieve this by: // 1. Rolling back any unsaved changes (ensures clean state) // 2. Triggering listeners to re-fetch data (which will read from disk) modelContext.rollback() // Notify listeners to re-fetch their data for closure in editedDataClosure { closure() } Self.logger.debug("Refreshed data from disk") } }