CRUD en Core Data
Las operaciones más básicas con Core Data van a ser las de crear, obtener, actualizar y borrar objetos persistentes, es decir operaciones del tipo Create/Read/Update/Delete
o CRUD. Aunque ya hemos explicado algo de esto en la sección anterior, vamos a verlo aquí de modo más sistemático.
Creación
En nuestro Swift estamos acostumbrados a crear objetos llamando simplemente al inicializador correspondiente, y podríamos estar tentados de hacer algo parecido con los objetos de Core Data, por ejemplo
var u = Usuario()
u.login = "Pepe"
...
Pero esto no lo podemos hacer con los objetos persistentes, porque Core Data es quien debe gestionar su ciclo de vida y por tanto los debe "tener controlados" en todo momento, desde su creación. Lo adecuado es pedirle a Core Data que cree el objeto para nosotros. Ya hemos visto varias veces cómo se hace esto, con el método insertNewObject(forEntityName:)
, pero cuando tenemos clases propias podemos usar el inicializador init(context:)
//para insertar un objeto, recordar que necesitamos el contexto de Core Data
let u = Usuario(context:miContexto)
//también se podría hacer con
//let u = NSEntityDescription.insertNewObject(forEntityName: "Usuario", into: miContexto) as! Usuario
u.login = "Pepe"
Actualización
Una vez tenemos un objeto gestionado (sea nuevo, con insertNewObject
o ya existente, con una fetch request) podemos cambiar sus propiedades y establecer relaciones con otros objetos. No obstante los cambios solo se harán persistentes cuando guardemos el contexto de persistencia con save
:
//suponemos "usuario" objeto gestionado por Core Data
//es decir, se ha obtenido con `insertNewObject` o una fetch request
usuario.login=@"moviles";
usuario.password=@"123456";
//AHORA es cuando se guardan las modificaciones de modo persistente
try! miContexto.save()
Dicho de otro modo, los cambios que hacemos en los objetos son cambios únicamente dentro del contexto (o si se quiere decir así, “en la memoria”). Cuando usamos save
los cambios se hacen también en el almacenamiento (store).
Relaciones "a uno"
Las relaciones "uno a uno" se representan con propiedades que son objetos "individuales". Por ejemplo un Mensaje
pertenece a un Usuario
a través de la relación usuario
. De este modo, podemos tratar la relación simplemente como una propiedad más.
mensaje.usuario = nuevoUsuario
Recordemos que normalmente las relaciones son bidireccionales. Si establecemos una relación entre objetos, Core Data se encargará automáticamente de actualizar la inversa.
let m = Mensaje(context:miContexto)
m.texto = "hola amigos"
m.fecha = Date()
//Supongamos "u" un objeto Usuario gestionado por Core Data
//Establecemos una relación Mensaje->Usuario
m.usuario = u;
//Core Data hace lo propio con Usuario->>Mensaje (1 a N)
print("Mensajes del usuario \(u.login)")
for mensaje in u.mensajes {
print("\(mensaje.fecha) \(mensaje.texto)")
}
En el ejemplo anterior decimos que un mensaje pertenece a un determinado usuario estableciendo la relación que va de Mensaje->Usuario. Core Data hace lo propio con la inversa, que va de Usuario->>Mensaje, de modo que si accedemos al usuario e iteramos por los mensajes, veremos el nuevo mensaje que hemos añadido.
Relaciones “a muchos”
Como ya vimos al crear el modelo de datos, si usamos clases propias en el modelo, por cada relación del tipo "a muchos" se genera una propiedad del tipo NSSet
(NSOrderedSet
si la relación es ordenada). Nótese que son clases inmutables. Si queremos añadir o eliminar elementos a la relación tendremos que copiar la propiedad en un NSMutableSet
o NSMutableOrderedSet
, hacer la modificación y luego asignar el nuevo conjunto a la propiedad.
Afortunadamente hay una forma mucho más sencilla de añadir/eliminar elementos, usar los métodos addToXXX
y removeFromXXX
obtenidos al generar las clases del modelo. Por ejemplo:
//c1 y u son objetos gestionados por Core Data
var c1 : Conversacion
var u : Usuario
//Supongamos que aquí los creamos en el contexto de persistencia
...
//c1 es un objeto gestionado por Core Data
c1.comienzo = Date();
//El usuario empieza a participar en una conversación
//Usamos el método de acceso generado por Core Data
u.addToConversaciones(c1);
...
Lectura
Para recuperar un objeto gestionado por Core Data normalmente se usan fetch requests, que en un entorno de BD se correspondería con las consultas. En sucesivas sesiones veremos con más detalle la sintaxis, pero por ejemplo obtener todos los objetos de una determinada entidad es sencillo, ya que no hay que especificar condición alguna, solo crear y ejecutar la fetch request:
//Creamos la fetch request y decimos que devuelve usuarios
let request : NSFetchRequest<Usuario> = NSFetchRequest(entityName:"Usuario")
//La ejecutamos (deberíamos detectar errores con do...catch, es para acortar el ejemplo)
let usuarios = try! miContexto.fetch(request)
//recorremos los resultados
for usuario in usuarios {
print(usuario.login!)
}
En las clases generadas por Xcode tenemos un método de utilidad llamado
fetchRequest
que simplifica el trabajo de la primera línea del ejemplo anterior. Así, podemos hacer simplementelet request = Usuario.fetchRequest()
Borrado
Podemos eliminar un objeto del contexto llamando a delete
sobre el contexto y pasándole el objeto a eliminar
miContexto.delete(usuario)
Cuidado, delete
no elimina el objeto del almacén persistente. Para eso tendremos que ejecutar save
, como cuando modificamos el objeto y queremos guardar los cambios.
Eliminar el objeto del contexto implica que se ejecuten las reglas de borrado. Así, si por ejemplo hubiéramos usado la regla Cascade
para la relación que va desde Conversacion
a Mensaje
esto implicaría que al borrar una conversación del contexto también se eliminarían todos sus mensajes.
No obstante, es posible que tras ejecutar delete
las actualizaciones no se propaguen de manera inmediata por el grafo de objetos del contexto. Para forzar esta propagación simplemente hay que llamar al método processPendingChanges()
del contexto.