import Foundation import CoreData /// Per-source recurring monthly contribution amount. /// /// Backed by the `InvestmentSource.monthlyContribution` Core Data attribute so the value /// syncs across devices via iCloud/CloudKit. Previously stored in `UserDefaults`, which is /// device-local and did NOT sync — `migrateIfNeeded(context:)` moves any legacy value over. enum MonthlyContributionStore { /// Legacy UserDefaults key (pre-iCloud). Only read once during migration. private static let legacyKey = "monthlyContributions" private static var viewContext: NSManagedObjectContext { CoreDataStack.shared.viewContext } static func contribution(for sourceId: UUID) -> Decimal? { guard let source = fetchSource(sourceId), let amount = source.monthlyContribution?.decimalValue, amount > 0 else { return nil } return amount } static func setContribution(_ amount: Decimal?, for sourceId: UUID) { guard let source = fetchSource(sourceId) else { return } if let amount, amount > 0 { source.monthlyContribution = NSDecimalNumber(decimal: amount) } else { source.monthlyContribution = nil } try? viewContext.save() } private static func fetchSource(_ id: UUID) -> InvestmentSource? { let request = InvestmentSource.fetchRequest() request.predicate = NSPredicate(format: "id == %@", id as CVarArg) request.fetchLimit = 1 return try? viewContext.fetch(request).first } // MARK: - Migration /// One-time migration of legacy UserDefaults contributions into Core Data so they sync /// via iCloud. Runs on every launch but is a no-op once the legacy key is cleared. static func migrateIfNeeded(context: NSManagedObjectContext) { guard let data = UserDefaults.standard.data(forKey: legacyKey), let dict = try? JSONDecoder().decode([String: Double].self, from: data), !dict.isEmpty else { return } let sources = (try? context.fetch(InvestmentSource.fetchRequest())) ?? [] var changed = false for source in sources where source.monthlyContribution == nil { if let raw = dict[source.id.uuidString], raw > 0 { source.monthlyContribution = NSDecimalNumber(value: raw) changed = true } } if changed { try? context.save() } UserDefaults.standard.removeObject(forKey: legacyKey) } }