Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Core Data
Code

Core Data y Swift: relaciones y más Fetching

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Core Data and Swift.
Core Data and Swift: Managed Objects and Fetch Requests
Core Data and Swift: NSFetchedResultsController

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

En el artículo anterior, aprendimos sobre NSManagedObject y lo fácil que es crear, leer, actualizar y eliminar registros usando Core Data. Sin embargo, no mencioné las relaciones en esa discusión. Aparte de algunas advertencias que debes tener en cuenta, las relaciones son tan fáciles de manipular como los atributos. En este artículo, nos centraremos en las relaciones y también continuaremos nuestra exploración de NSFetchRequest.

Prerrequisitos

Lo que cubro en esta serie sobre Core Data es aplicable a iOS 7+ y OS X 10.10+, pero el enfoque estará en iOS. En esta serie, trabajaré con Xcode 7.1 y Swift 2.1. Si prefiere Objective-C, le recomiendo que lea mis series anteriores en el marco de Core Data.

1. relaciones

Ya hemos trabajado con las relaciones en el editor de modelos Core Data y, por lo tanto, lo que voy a decirles resultará familiar. Las relaciones son, al igual que los atributos, accedidas mediante la codificación de clave-valor. Recuerde que el modelo de datos que creamos anteriormente en esta serie define una entidad de persona y una entidad de dirección. Una persona está  vinculada a una o más direcciones y una dirección está vinculada a una o más personas. Esta es una relación de muchos a muchos.

Para obtener las direcciones de una persona, simplemente invocamos valueForKey(_:) en la persona, una instancia de NSManagedObject, y pasamos las direcciones como la clave. Tenga en cuenta que las direcciones es la clave que definimos en el modelo de datos. ¿Qué tipo de objeto esperas? La mayoría de las personas que son nuevas en Core Data esperan una matriz ordenada, pero Core Data devuelve un conjunto, que no está clasificado. Trabajar con conjuntos tiene sus ventajas, como verás más adelante.

Creación de registros

Basta con la teoría, abra el proyecto del artículo anterior o clone desde GitHub. Comencemos por crear una persona y luego vincularla a una dirección. Para crear una persona, abra AppDelegate.swift y actualice la application(_:didFinishLaunchingWithOptions:) como se muestra a continuación.

Esto debería parecer familiar si has leído el artículo anterior. Crear una dirección es similar a lo que puedes ver a continuación.

Debido a que cada atributo de la entidad Address está marcado como opcional, no necesitamos asignar un valor a cada atributo. En el ejemplo, solo establecemos los atributos de calle y ciudad del registro.

Creando una relación

Para vincular newAddress a newPerson, invocamos valueForKey(_:), pasando las addresses como la clave. El valor que pasamos es una instancia de NSSet que contiene newAddress. Eche un vistazo al siguiente bloque de código para aclararlo.

Llamamos a save() en el contexto del objeto administrado de newPerson para propagar los cambios al almacén persistente. Recuerde que llamar a save() en un contexto de objeto gestionado guarda el estado del contexto de objeto gestionado. Esto significa que newAddress también se escribe en la tienda de respaldo, así como las relaciones que acabamos de definir.

Quizás se esté preguntando por qué no vinculamos newPerson a newAddress, porque definimos una relación inversa en el modelo de datos. Core Data crea esta relación para nosotros. Si una relación tiene una relación inversa, entonces Core Data se encarga de esto automáticamente. Puedes verificar esto preguntando a newAddress para sus persons.

Obtención y actualización de una relación

Actualizar una relación no es difícil tampoco. La única advertencia es que necesitamos agregar o eliminar elementos de las inmutables manos de Datos de Core de NSSet. Para facilitar esta tarea, sin embargo, el protocolo NSKeyValueCoding declara un método conveniente mutableSetValueForKey(_:), que devuelve un objeto NSMutableSet. Luego podemos simplemente agregar o eliminar un elemento de la colección para actualizar la relación.

Eche un vistazo al siguiente bloque de código en el que creamos otra dirección y asócielo con newPerson. Hacemos esto invocando mutableSetValueForKey(_:) en newPerson y agregando otherAddress al conjunto mutable. No es necesario decirle a Core Data que hemos actualizado la relación. Core Data realiza un seguimiento del conjunto mutable que nos proporcionó y actualiza la relación.

Borrar una relación

Puede eliminar una relación invocando setValue(_:forKey:), pasando nil como el valor y el nombre de la relación como la clave. En el siguiente fragmento de código, desvincularemos todas las direcciones de newPerson.

2. Relaciones uno-a-uno y uno-a-muchos

Relaciones Uno a Uno

Aunque nuestro modelo de datos no define una relación de uno a uno, ha aprendido todo lo que necesita saber para trabajar con este tipo de relación. Trabajar con una relación de uno a uno es idéntico a trabajar con atributos. La única diferencia es que el valor que obtiene de valueForKey(_:) y el valor que pasa a setValue(_:forKey:) es una instancia de NSManagedObject.

Vamos a actualizar el modelo de datos para ilustrar esto. Abra Core_Data.xcdatamodeld y seleccione la entidad Person. Crea una nueva relación y llámala spouse. Establezca la entidad Person como destino y establezca la relación de spouse como la relación inversa.

Add a One-To-One Relationship to Person

Como puede ver, es posible crear una relación en la que el destino de la relación sea la misma entidad que la entidad que define la relación. También tenga en cuenta que siempre configuramos el inverso de la relación. Como indica la documentación de Apple, hay muy pocas situaciones en las que desea crear una relación que no tenga una relación inversa.

¿Sabe lo que ocurrirá si tuviera que compilar y ejecutar la aplicación? Así es, la aplicación fallaria. Debido a que cambiamos el modelo de datos, el almacén de respaldo existente, una base de datos SQLite en este ejemplo, ya no es compatible con el modelo de datos. Para remediar esto, elimine la aplicación de su dispositivo o del simulador y ejecute la aplicación. Sin embargo, no se preocupe, resolveremos este problema de manera más elegante en una futura instalación que use migraciones.

Si puede ejecutar la aplicación sin problemas, entonces es el momento para el siguiente paso. Regrese a la clase de delegado de la aplicación y agregue el siguiente bloque de código.

Para establecer a anotherPerson como el cónyuge de newPerson, invocamos setValue(_:forKey:) en newPerson y pasamos a anotherPerson y "spouse" como argumentos. Podemos lograr el mismo resultado invocando setValue(_:forKey:) en anotherPerson, pasando newPerson y "spouse" como argumentos.

Relaciones uno a muchos

Terminemos con un vistazo a las relaciones de uno a muchos. Abra Core_Data.xcdatamodeld, seleccione la entidad Person y cree una relación llamada children. Establezca el destino en Person, establezca el tipo en To Many, y deje la relación inversa vacía por ahora.

Add a One-To-Many Relationship to Person

Cree otra relación llamada father, establezca el destino como Person, y establezca la relación inversa con children. Esto llenará automáticamente la relación inversa de la relación de children que dejamos en blanco hace un momento. Ahora hemos creado una relación de uno a muchos, es decir, un padre puede tener muchos hijos, pero un hijo solo puede tener un padre biológico.

Add a One-To-Many Relationship to Person

Vuelve al delegado de la aplicación y agrega el siguiente bloque de código. Creamos otro registro Person, establecemos sus atributos y lo establecemos como hijo de newPerson solicitando a Core Data un conjunto mutable para los children clave y agregando el nuevo registro al conjunto mutable.

El siguiente bloque de código logra el mismo resultado al establecer el atributo father de anotherChildPerson. El resultado es que newPerson se convierte en el padre de anotherChildPerson y anotherChildPerson se convierte en un hijo de newPerson.

3. Más ir a buscar

El modelo de datos de nuestra aplicación de muestra ha crecido bastante en términos de complejidad. Hemos creado relaciones de uno a uno, de uno a muchos y de muchos a muchos. Hemos visto lo fácil que es crear registros, incluidas las relaciones. Si también queremos poder extraer esos datos del almacén persistente, entonces necesitamos aprender más sobre la obtención de datos. Comencemos con un ejemplo simple en el que vemos cómo ordenar los resultados devueltos por una solicitud.

Ordenar descriptores

Para ordenar los registros que obtenemos del contexto del objeto administrado, usamos la clase NSSortDescriptor. Echa un vistazo al siguiente fragmento de código.

Inicializamos una solicitud de recuperación pasando la entidad que nos interesa, Person. Luego creamos un objeto NSSortDescriptor invocando init(key:ascending:), pasando el atributo de la entidad por la que nos gustaría clasificar, primero, y un booleano que indica si los registros deben ordenarse en orden ascendente o descendente.

Vincularemos el descriptor de ordenación a la solicitud de recuperación configurando la propiedad sortDescriptors de la solicitud. Debido a que la propiedad sortDescriptors es de tipo [NSSortDescriptor]?, es posible especificar más de un descriptor de clasificación. Vamos a echar un vistazo a esta opción en un momento.

El resto del bloque de código debe parecer familiar. La solicitud de recuperación se pasa al contexto del objeto gestionado, que ejecuta la solicitud de recuperación cuando invocamos executeFetchRequest(_:). Recuerde que este último es un método de lanzamiento, lo que significa que usamos la palabra clave try y ejecutamos la solicitud de recuperación en una declaración do-catch.

Ejecute la aplicación e inspeccione la salida en la consola de Xcode. La salida debe ser similar a la que se muestra a continuación. Como puede ver, los registros están ordenados por su nombre.

Si ve duplicados en la salida, asegúrese de comentar el código que escribimos anteriormente para crear los registros. Cada vez que ejecuta la aplicación, se crean los mismos registros, lo que da como resultado registros duplicados.

Como mencioné, es posible combinar múltiples descriptores de clasificación. Vamos a ordenar los registros por su apellido y edad. Primero establecemos la clave del primer descriptor de clasificación en último. Luego creamos otro descriptor de clasificación con una clave de edad y lo agregamos a la gama de descriptores de clasificación.

La salida muestra que el orden de los descriptores de ordenación en la matriz es importante. Los registros se ordenan primero por su apellido y luego por su edad.

Predicados

Los descriptores de clasificación son geniales y fáciles de usar, pero los predicados son lo que realmente hace que la obtención de datos sea poderosa en Core Data. Los descriptores de clasificación le dicen a Core Data cómo deben ordenarse los registros. Los predicados le dicen a Core Data qué registros le interesan. La clase con la que trabajaremos es NSPredicate.

Empecemos por buscar a todos los miembros de la familia Doe. Esto es muy fácil de hacer y la sintaxis le recordará a algunos de usted SQL.

No hemos cambiado mucho aparte de crear un objeto NSPredicate invocando init(format:arguments:) y vinculando el predicado a la solicitud de recuperación al establecer la propiedad predicate de este último. Tenga en cuenta que el método init(format:arguments:) acepta un número variable de argumentos.

La cadena de formato de predicado usa %K para el nombre de la propiedad y %@ para el valor. Como se indica en la Guía de programación de predicados, %K es una sustitución de argumento variable para una ruta de acceso clave, mientras que %@ es una sustitución de argumento variable para un valor de objeto. Esto significa que la cadena de formato de predicado de nuestro ejemplo se evalúa como last == "Doe".

Si ejecuta la aplicación e inspecciona la salida en la consola de Xcode, debería ver el siguiente resultado:

Hay muchos operadores que podemos usar para comparar. Además de = y ==, que son idénticos en lo que respecta a Core Data, también hay >= y =>, <= y =>, != y <>, y > y <. Le animo a experimentar con estos operadores para saber cómo afectan los resultados de la solicitud.

El siguiente predicado ilustra cómo podemos usar el operador >= para obtener solo registros Person con un atributo de edad superior a 30.

También contamos con operadores para la comparación de cadenas, CONTAINS, LIKE, MATCHES, BEGINSWITH, y ENDSWITH. Vamos a buscar cada registro Person cuyo nombre CONTAINS la letra j.

Si ejecuta la aplicación, la matriz de resultados estará vacía, ya que la comparación de cadenas distingue entre mayúsculas y minúsculas de forma predeterminada. Podemos cambiar esto agregando un modificador así:

También puede crear predicados compuestos utilizando las palabras clave AND, OR, y NOT. En el siguiente ejemplo, buscamos a todas las personas cuyo nombre contenga la letra j y tengan menos de 30 años.

Los predicados también facilitan la obtención de registros en función de su relación. En el siguiente ejemplo, buscamos a todas las personas cuyo nombre de padre es igual a Bart.

El predicado anterior funciona como se esperaba, porque %K es una sustitución de argumento variable para una ruta de acceso clave, no solo una clave.

Lo que debe recordar es que los predicados le permiten consultar la tienda de respaldo sin que sepa nada sobre la tienda. Aunque la sintaxis de la cadena de formato de predicado recuerda a SQL de alguna manera, no importa si el almacén de respaldo es una base de datos SQLite o un almacén en memoria. Este es un concepto muy poderoso que no es exclusivo de Core Data. El registro activo de Rails es otro buen ejemplo de este paradigma.

Hay mucho más en los predicados de lo que te he mostrado en este artículo. Si desea obtener más información sobre los predicados, le sugiero que tome un pico en la Guía de programación de predicados de Apple. También trabajaremos más con predicados en los próximos artículos de esta serie.

Conclusión

Ahora tiene un buen conocimiento de los conceptos básicos de Core Data y es hora de comenzar a trabajar con el marco creando una aplicación que aproveche la potencia de Core Data. En el siguiente artículo, nos encontramos con otra clase importante del marco de Core Data, NSFetchedResultsController. Esta clase nos ayudará a administrar una colección de registros, pero aprenderá que hace un poco más que eso.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.