Primera build enviada
This commit is contained in:
@@ -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)")
|
||||
|
||||
Reference in New Issue
Block a user