Files
Reflect/Shared/Persisence/DataControllerDELETE.swift
Trey t 0442eab1f8 Rebrand entire project from Feels to Reflect
Complete rename across all bundle IDs, App Groups, CloudKit containers,
StoreKit product IDs, data store filenames, URL schemes, logger subsystems,
Swift identifiers, user-facing strings (7 languages), file names, directory
names, Xcode project, schemes, assets, and documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:47:16 -06:00

135 lines
4.6 KiB
Swift

//
// DataControllerDELETE.swift
// Reflect
//
// SwiftData DELETE operations.
//
import SwiftData
import Foundation
import os.log
extension DataController {
// MARK: - Photo Cleanup
func cleanupPhotoIfNeeded(for entry: MoodEntryModel) {
if let photoID = entry.photoID {
if !PhotoManager.shared.deletePhoto(id: photoID) {
AppLogger.general.warning("Failed to delete orphaned photo \(photoID.uuidString) for entry on \(entry.forDate)")
}
}
}
// MARK: - Delete Operations
func clearDB() {
do {
// Clean up photo files before batch delete
let descriptor = FetchDescriptor<MoodEntryModel>()
if let allEntries = try? modelContext.fetch(descriptor) {
for entry in allEntries {
cleanupPhotoIfNeeded(for: entry)
}
}
try modelContext.delete(model: MoodEntryModel.self)
saveAndRunDataListeners()
AnalyticsManager.shared.track(.allDataCleared)
} catch {
AppLogger.general.error("Failed to clear database: \(error.localizedDescription)")
}
}
func deleteLast(numberOfEntries: Int) {
guard let startDate = Calendar.current.date(byAdding: .day, value: -numberOfEntries, to: Date()) else { return }
let entries = getData(startDate: startDate, endDate: Date(), includedDays: [])
for entry in entries {
cleanupPhotoIfNeeded(for: entry)
modelContext.delete(entry)
}
saveAndRunDataListeners()
}
func deleteRandomFromLast(numberOfEntries: Int) {
guard let startDate = Calendar.current.date(byAdding: .day, value: -numberOfEntries, to: Date()) else { return }
let entries = getData(startDate: startDate, endDate: Date(), includedDays: [])
for entry in entries where Bool.random() {
cleanupPhotoIfNeeded(for: entry)
modelContext.delete(entry)
}
saveAndRunDataListeners()
}
/// Get ALL entries for a specific date (not just the first one)
func getAllEntries(byDate date: Date) -> [MoodEntryModel] {
let startDate = Calendar.current.startOfDay(for: date)
let endDate = Calendar.current.date(byAdding: .day, value: 1, to: startDate)!
let descriptor = FetchDescriptor<MoodEntryModel>(
predicate: #Predicate { entry in
entry.forDate >= startDate && entry.forDate < endDate
},
sortBy: [SortDescriptor(\.forDate, order: .forward)]
)
return (try? modelContext.fetch(descriptor)) ?? []
}
/// Delete ALL entries for a specific date
func deleteAllEntries(forDate date: Date) {
let entries = getAllEntries(byDate: date)
for entry in entries {
cleanupPhotoIfNeeded(for: entry)
modelContext.delete(entry)
}
saveAndRunDataListeners()
}
/// Find and remove duplicate entries, keeping only the most recent for each date
/// Returns the number of duplicates removed
@discardableResult
func removeDuplicates() -> Int {
let allEntries = getData(startDate: Date(timeIntervalSince1970: 0), endDate: Date(), includedDays: [])
// Group by day
var entriesByDay = [Date: [MoodEntryModel]]()
let calendar = Calendar.current
for entry in allEntries {
let dayStart = calendar.startOfDay(for: entry.forDate)
entriesByDay[dayStart, default: []].append(entry)
}
var duplicatesRemoved = 0
for (_, dayEntries) in entriesByDay where dayEntries.count > 1 {
// Sort by timestamp (most recent first), preferring non-missing moods
let sorted = dayEntries.sorted { a, b in
// Prefer non-missing moods
if a.mood != .missing && b.mood == .missing { return true }
if a.mood == .missing && b.mood != .missing { return false }
// Then by timestamp (most recent first)
return a.timestamp > b.timestamp
}
// Keep the first (best) entry, delete the rest
for entry in sorted.dropFirst() {
cleanupPhotoIfNeeded(for: entry)
modelContext.delete(entry)
duplicatesRemoved += 1
}
}
if duplicatesRemoved > 0 {
saveAndRunDataListeners()
AppLogger.general.info("Removed \(duplicatesRemoved) duplicate entries")
AnalyticsManager.shared.track(.duplicatesRemoved(count: duplicatesRemoved))
}
return duplicatesRemoved
}
}