¿Qué hace core-data-expert?
Guía experta de Core Data (iOS/macOS): configuración del stack, solicitudes de recuperación y NSFetchedResultsController, guardado y conflictos de fusión, hilos y Swift Concurrency, operaciones por lote e historial persistente, migraciones, rendimiento y sincronización con NSPersistentCloudKitContainer/CloudKit.
Configuración del Stack
Stack básico
import CoreData
class CoreDataStack {
static let shared = CoreDataStack()
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Modelo")
container.loadPersistentStores { storeDescription, error in
if let error = error as NSError? {
fatalError("Error al cargar la tienda persistente: \(error)")
}
}
return container
}()
var context: NSManagedObjectContext {
persistentContainer.viewContext
}
func saveContext() {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Error al guardar el contexto: \(nserror)")
}
}
}
}
Stack con CloudKit
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "Modelo")
container.loadPersistentStores { storeDescription, error in
if let error = error as NSError? {
fatalError("Error al cargar la tienda: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
Solicitudes de Recuperación (Fetch Requests) y NSFetchedResultsController
Solicitud básica
let request: NSFetchRequest<Entidad> = Entidad.fetchRequest()
request.predicate = NSPredicate(format: "atributo == %@", valor)
request.sortDescriptors = [NSSortDescriptor(key: "fecha", ascending: false)]
NSFetchedResultsController
let fetchRequest: NSFetchRequest<Entidad> = Entidad.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "nombre", ascending: true)]
let controller = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil
)
controller.delegate = self
try controller.performFetch()
Guardado y Conflictos de Fusión
Estrategias de fusión
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
// O NSMergeByPropertyStoreTrumpMergePolicy
// O NSOverwriteMergePolicy
// O NSRollbackMergePolicy
Guardado en segundo plano
let privateContext = persistentContainer.newBackgroundContext()
privateContext.perform {
// Realizar operaciones aquí
do {
try privateContext.save()
} catch {
print("Error al guardar: \(error)")
}
}
Hilos y Swift Concurrency
Uso de async/await
func fetchEntidadesAsync() async throws -> [Entidad] {
let context = persistentContainer.newBackgroundContext()
return try await context.perform {
let request: NSFetchRequest<Entidad> = Entidad.fetchRequest()
return try context.fetch(request)
}
}
Contextos hijo
let childContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
childContext.parent = parentContext
Operaciones por Lote e Historial Persistente
Eliminación por lote
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest as! NSFetchRequest<NSFetchRequestResult>)
try context.execute(deleteRequest)
Historial persistente
let historyToken: NSPersistentHistoryToken?
let fetchHistoryRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: historyToken)
let result = try context.execute(fetchHistoryRequest) as? NSPersistentHistoryResult
Migraciones
Migración ligera
let options = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
container.persistentStoreDescriptions.forEach { description in
description.setOption(true, forKey: NSMigratePersistentStoresAutomaticallyOption)
description.setOption(true, forKey: NSInferMappingModelAutomaticallyOption)
}
Migración personalizada
- Crear un nuevo modelo de datos
- Crear un modelo de mapeo
- Implementar
NSEntityMigrationPolicy
Rendimiento
- Lazy loading: Usar relaciones de manera eficiente
- Batch fetching:
fetchBatchSizepara conjuntos grandes - Faulting:
setRelationshipKeyPaths(forPrefetching:) - Caché: Usar
NSFetchedResultsControllercon caché
Batch fetching
request.fetchBatchSize = 20
Sincronización con NSPersistentCloudKitContainer
Configuración básica CloudKit
let container = NSPersistentCloudKitContainer(name: "Modelo")
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Error: \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
Notas importantes sobre CloudKit
- Asegurar que el modelo de datos tenga un
NSPersistentCloudKitContainercomo tipo de contenedor - Crear un esquema de CloudKit en el panel de CloudKit Dashboard
- Manejar errores de red y conflictos
- Probar con cuentas de iCloud reales