Primera build enviada

This commit is contained in:
2026-01-19 14:40:43 +01:00
parent c6be398e5a
commit b03d35194f
36 changed files with 1641 additions and 561 deletions
@@ -2,11 +2,14 @@ import Foundation
import CoreData
import Combine
@MainActor
class InvestmentSourceRepository: ObservableObject {
private let context: NSManagedObjectContext
@Published private(set) var sources: [InvestmentSource] = []
private var cancellables = Set<AnyCancellable>()
init(context: NSManagedObjectContext = CoreDataStack.shared.viewContext) {
self.context = context
fetchSources()
@@ -14,43 +17,33 @@ class InvestmentSourceRepository: ObservableObject {
}
private func setupNotificationObserver() {
NotificationCenter.default.addObserver(
self,
selector: #selector(contextDidChange),
name: .NSManagedObjectContextObjectsDidChange,
object: context
)
}
@objc private func contextDidChange(_ notification: Notification) {
guard isRelevantChange(notification) else { return }
fetchSources()
NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: context)
.receive(on: DispatchQueue.main)
.sink { [weak self] notification in
guard let self, self.isRelevantChange(notification) else { return }
self.fetchSources()
}
.store(in: &cancellables)
}
// MARK: - Fetch
func fetchSources(account: Account? = nil) {
context.perform { [weak self] in
guard let self else { return }
let request: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
if let account = account {
request.predicate = NSPredicate(format: "account == %@", account)
}
request.sortDescriptors = [
NSSortDescriptor(keyPath: \InvestmentSource.name, ascending: true)
]
let request: NSFetchRequest<InvestmentSource> = InvestmentSource.fetchRequest()
if let account = account {
request.predicate = NSPredicate(format: "account == %@", account)
}
request.sortDescriptors = [
NSSortDescriptor(keyPath: \InvestmentSource.name, ascending: true)
]
// Performance: Add batch size for large datasets
request.fetchBatchSize = 100
do {
let fetched = try self.context.fetch(request)
DispatchQueue.main.async {
self.sources = fetched
}
} catch {
print("Failed to fetch sources: \(error)")
DispatchQueue.main.async {
self.sources = []
}
}
do {
sources = try context.fetch(request)
} catch {
print("Failed to fetch sources: \(error)")
sources = []
}
}
@@ -82,6 +75,28 @@ class InvestmentSourceRepository: ObservableObject {
// MARK: - Create
/// Check if a source with the given name already exists in the account (case-insensitive)
func sourceExists(name: String, in account: Account?) -> Bool {
let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
let normalized = trimmed.lowercased()
return sources.contains { source in
let nameMatches = source.name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == normalized
let accountMatches = source.account?.id == account?.id
return nameMatches && accountMatches
}
}
/// Find an existing source by name (case-insensitive) within the same account
func findSource(byName name: String, in account: Account?) -> InvestmentSource? {
let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
let normalized = trimmed.lowercased()
return sources.first { source in
let nameMatches = source.name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == normalized
let accountMatches = source.account?.id == account?.id
return nameMatches && accountMatches
}
}
@discardableResult
func createSource(
name: String,
@@ -90,8 +105,13 @@ class InvestmentSourceRepository: ObservableObject {
customFrequencyMonths: Int = 1,
account: Account? = nil
) -> InvestmentSource {
// Check if source with same name already exists in this account
if let existing = findSource(byName: name, in: account) {
return existing
}
let source = InvestmentSource(context: context)
source.name = name
source.name = name.trimmingCharacters(in: .whitespacesAndNewlines)
source.category = category
source.notificationFrequency = notificationFrequency.rawValue
source.customFrequencyMonths = Int16(customFrequencyMonths)
@@ -188,7 +208,8 @@ class InvestmentSourceRepository: ObservableObject {
guard context.hasChanges else { return }
do {
try context.save()
fetchSources()
// Note: Removed redundant fetchSources() call - the NotificationCenter observer
// in setupNotificationObserver() already handles refetching on context changes
CoreDataStack.shared.refreshWidgetData()
} catch {
print("Failed to save context: \(error)")