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,53 +2,47 @@ import Foundation
import CoreData
import Combine
@MainActor
class CategoryRepository: ObservableObject {
private let context: NSManagedObjectContext
@Published private(set) var categories: [Category] = []
private var cancellables = Set<AnyCancellable>()
init(context: NSManagedObjectContext = CoreDataStack.shared.viewContext) {
self.context = context
fetchCategories()
ensureDefaultCategoriesExist()
setupNotificationObserver()
}
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 }
fetchCategories()
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.fetchCategories()
}
.store(in: &cancellables)
}
// MARK: - Fetch
func fetchCategories() {
context.perform { [weak self] in
guard let self else { return }
let request: NSFetchRequest<Category> = Category.fetchRequest()
request.sortDescriptors = [
NSSortDescriptor(keyPath: \Category.sortOrder, ascending: true),
NSSortDescriptor(keyPath: \Category.name, ascending: true)
]
let request: NSFetchRequest<Category> = Category.fetchRequest()
request.sortDescriptors = [
NSSortDescriptor(keyPath: \Category.sortOrder, ascending: true),
NSSortDescriptor(keyPath: \Category.name, ascending: true)
]
// Performance: Add batch size for fetches
request.fetchBatchSize = 50
do {
let fetched = try self.context.fetch(request)
DispatchQueue.main.async {
self.categories = fetched
}
} catch {
print("Failed to fetch categories: \(error)")
DispatchQueue.main.async {
self.categories = []
}
}
do {
categories = try context.fetch(request)
} catch {
print("Failed to fetch categories: \(error)")
categories = []
}
}
@@ -61,14 +55,33 @@ class CategoryRepository: ObservableObject {
// MARK: - Create
/// Check if a category with the given name already exists (case-insensitive)
func categoryExists(name: String) -> Bool {
let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
let normalized = trimmed.lowercased()
return categories.contains { $0.name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == normalized }
}
/// Find an existing category by name (case-insensitive)
func findCategory(byName name: String) -> Category? {
let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
let normalized = trimmed.lowercased()
return categories.first { $0.name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() == normalized }
}
@discardableResult
func createCategory(
name: String,
colorHex: String,
icon: String
) -> Category {
// Check if category with same name already exists
if let existing = findCategory(byName: name) {
return existing
}
let category = Category(context: context)
category.name = name
category.name = name.trimmingCharacters(in: .whitespacesAndNewlines)
category.colorHex = colorHex
category.icon = icon
category.sortOrder = Int16(categories.count)
@@ -78,8 +91,29 @@ class CategoryRepository: ObservableObject {
}
func createDefaultCategoriesIfNeeded() {
guard categories.isEmpty else { return }
Category.createDefaultCategories(in: context)
ensureDefaultCategoriesExist()
}
func ensureDefaultCategoriesExist() {
var addedCount = 0
var nextSortOrder = categories.count
for categoryData in Category.defaultCategories {
if categoryExists(name: categoryData.name) {
continue
}
let category = Category(context: context)
category.name = categoryData.name
category.colorHex = categoryData.colorHex
category.icon = categoryData.icon
category.sortOrder = Int16(nextSortOrder)
nextSortOrder += 1
addedCount += 1
}
guard addedCount > 0 else { return }
save()
fetchCategories()
}
@@ -148,7 +182,8 @@ class CategoryRepository: ObservableObject {
guard context.hasChanges else { return }
do {
try context.save()
fetchCategories()
// Note: Removed redundant fetchCategories() call - the NotificationCenter observer
// in setupNotificationObserver() already handles refetching on context changes
CoreDataStack.shared.refreshWidgetData()
} catch {
print("Failed to save context: \(error)")