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, vamos a verlo aquí de modo más sistemático.
Creación¶
En nuestro código 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. Esto lo hacemos llamando al inicializador que recibe el contexto de persistencia como parámetro: 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 creado con init(context:)
o ya existente y recuperado 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 `init(context:)` o una fetch request
usuario.login="mastermoviles";
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 la siguiente sesión veremos con más detalle la sintaxis, pero por ejemplo obtener todas las instancias de una determinada entidad es sencillo, ya que no hay que especificar condición alguna, solo crear y ejecutar una fetch request del tipo deseado:
//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.