Advertisement
  1. Code
  2. Core Data

Core Data y Swift: Objetos gestionados y solicitudes de recuperación

by
Read Time:14 minsLanguages:
This post is part of a series called Core Data and Swift.
Core Data and Swift: Data Model
Core Data and Swift: Relationships and More Fetching

Spanish (Español) translation by Andrea J (you can also view the original English article)

Con todo lo relacionado con los modelos de datos de Cora Data aún frescos en tu mente, es hora de empezar a trabajar con Core Data. En este artículo, conocemos NSManagedObject, la clase con la que interactuarás más cuando trabajes con Core Data. Aprenderás a crear, leer, actualizar y eliminar registros.

También conocerás algunas otras clases de Core Data, como NSFetchRequest y NSEntityDescription. Permíteme comenzar presentándote a NSManagedObject, tu nuevo mejor amigo.

Requisitos previos

Lo que abarco 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 prefieres Objective-C, te recomiendo leer mi serie anterior sobre el framework Core Data.

1. Objetos gestionados

Las instancias de NSManagedObject representan un registro en la tienda de respaldo de Core Data. Recuerda, no importa cómo se vea esa tienda de respaldo. Sin embargo, para revisar la analogía de la base de datos, una instancia de NSManagedObject contiene la información de una fila en una tabla de base de datos.

La razón por la que Core Data usa NSManagedObject en lugar de NSObject como su clase base para modelar registros tendrá más sentido un poco más adelante. Antes de empezar a trabajar con NSManagedObject, necesitamos saber algunas cosas sobre esta clase.

NSEntityDescription

Cada instancia de NSManagedObject está asociada a una instancia de NSEntityDescription. La descripción de la entidad incluye información sobre el objeto administrado, como la entidad del objeto administrado, así como sus atributos y relaciones.

NSManagedObjectContext

Un objeto administrado también está vinculado a una instancia de NSManagedObjectContext. El contexto de objeto administrado al que pertenece un objeto administrado supervisa el objeto administrado para detectar cambios.

2. Crear un registro

Teniendo en cuenta lo anterior, crear un objeto administrado es bastante sencillo. Para asegurarte de que un objeto administrado está configurado correctamente, se recomienda usar el inicializador designado para crear nuevas instancias de NSManagedObject. Veamos cómo funciona esto creando un nuevo objeto persona.

Abre el proyecto del artículo anterior o clona el repositorio de GitHub. Debido a que no crearemos una aplicación funcional en este artículo, haremos la mayor parte de nuestro trabajo en la clase de delegado de la aplicación, AppDelegate. Abre AppDelegate.swift y actualiza la implementación de application(_:didFinishLaunchingWithOptions:) como se muestra a continuación.

Lo primero que hacemos es crear una instancia de la clase NSEntityDescription invocando entityForName(_:inManagedObjectContext:). Pasamos el nombre de la entidad para la que queremos crear un objeto administrado, "Person" y una instancia de NSManagedObjectContext.

¿Por qué necesitamos pasar un objeto NSManagedObjectContext? Especificamos el nombre para el que queremos crear un objeto administrado, pero también necesitamos indicarle a Core Data dónde puede encontrar el modelo de datos para esa entidad. Recuerda que un contexto de objeto administrado está vinculado a un coordinador de tienda persistente y un coordinador de tienda persistente mantiene una referencia a un modelo de datos. Cuando pasamos en un contexto de objeto administrado, Core Data pide a su coordinador de tienda persistente su modelo de datos para encontrar la entidad que estamos buscando.

En el segundo paso, invocamos el inicializador designado de la clase NSManagedObject, init(entity:insertIntoManagedObjectContext:). Pasamos la descripción de la entidad y una instancia de NSManagedObjectContext. ¿Espera? ¿Por qué necesitamos pasar otra instancia de NSManagedObjectContext? Recuerda lo que escribí antes. Un objeto administrado está asociado a una descripción de entidad y vive en un contexto de objeto administrado, por lo que le decimos a Core Data a qué contexto de objeto administrado se debe vincular el nuevo objeto administrado.

No es muy complejo. ¿Lo es? Ya creamos un nuevo objeto de persona. ¿Cómo cambiamos sus atributos o definimos una relación? Esto se hace aprovechando la codificación de valores clave. Para cambiar el nombre del nuevo objeto de persona que acabamos de crear, hacemos lo siguiente:

Si estás familiarizado con la codificación de valores clave, esto te resultará muy familiar. Dado que la clase NSManagedObject se ajusta al protocolo NSKeyValueCoding, configuramos un atributo invocando setValue(_:forKey:). Es así de simple.

Una desventaja de este enfoque es que puedes introducir errores fácilmente si escribes mal un atributo o el nombre de una relación. Además, Xcode no completa automáticamente los nombres de los atributos como, por ejemplo, los nombres de las propiedades. Este problema es fácil de resolver, pero es algo que veremos un poco más adelante en esta serie.

Antes de continuar con nuestra exploración de NSManagedObject, configuremos el atributo edad de newPerson en 44.

3. Guardar un registro

Aunque ahora tenemos una nueva instancia de persona, Core Data aún no ha guardado a la persona en su tienda de respaldo. Por el momento, el objeto administrado que creamos solo vive en el contexto de objeto administrado en el que se insertó. Para guardar el objeto persona en la tienda de respaldo, necesitamos guardar los cambios del contexto del objeto administrado llamando a save() en él.

El método save() es un método de lanzamiento y devuelve un booleano para indicar el resultado de la operación de guardar. Revisa el siguiente bloque de código para aclararlo.

Compila y ejecuta la aplicación para ver si todo funciona según lo esperado. ¿También sufriste un accidente? ¿Qué te dijo la salida de la consola? ¿Se parecía a la salida de abajo?

Xcode nos dice que esperaba una instancia NSDate para el primer atributo, pero pasamos un String. Si abres el modelo Core Data que creamos en el artículo anterior, verás que el tipo del primer atributo es Fecha. Cámbialo a String y ejecuta la aplicación una vez más.

¿Otro accidente? Aunque este es un tema más avanzado, es importante comprender lo que está sucediendo.

Compatibilidad del modelo de datos

La salida en la consola de Xcode debería ser similar a la salida que se muestra a continuación. Ten en cuenta que el error es diferente al anterior. Xcode nos dice que el modelo para abrir la tienda es incompatible con el utilizado para crear la tienda. ¿Cómo sucedió esto?

Cuando lanzamos la aplicación por primera vez hace unos momentos, Core Data inspeccionó el modelo de datos y, en base a ese modelo, creó una tienda para nosotros, una base de datos SQLite en este caso. Sin embargo, Core Data es inteligente. Se asegura de que la estructura de la tienda de respaldo y la del modelo de datos sean compatibles. Esto es vital para asegurarnos de que volvemos de la tienda de respaldo lo que esperamos y lo que pusimos allí en primer lugar.

Durante la primera falla, notamos que nuestro modelo de datos contenía un error y cambiamos el tipo del primer atributo de Fecha a String. En otras palabras, cambiamos el modelo de datos a pesar de que Core Data ya había creado la tienda de respaldo para nosotros en función del modelo de datos incorrecto.

Después de actualizar el modelo de datos, iniciamos la aplicación nuevamente y nos encontramos con la segunda falla. Una de las cosas que hace Core Data cuando crea la pila de Core Data es asegurarse de que el modelo de datos y la tienda de respaldo, si existe, sean compatibles. Ese no fue el caso en nuestro ejemplo, de ahí el accidente.

¿Cómo resolvemos esto? La solución fácil es eliminar la aplicación del dispositivo o del simulador y volver a iniciar la aplicación. Sin embargo, esto es algo que no puedes hacer si ya tienes una aplicación en la App Store que la gente está usando. En ese caso, haces uso de las migraciones, que es algo que discutiremos en un próximo artículo.

Debido a que no tenemos millones de usuarios usando nuestra aplicación, podemos eliminarla de forma segura de nuestro dispositivo de prueba y ejecutarla nuevamente. Si todo salió bien, la nueva persona ahora está almacenada de forma segura en la tienda, la base de datos SQLite Core Data creada para nosotros.

Inspección de la tienda de respaldo

Puedes verificar que la operación de guardar funcionó revisando dentro de la base de datos SQLite. Si ejecutaste la aplicación en el simulador, ve a /Users//Library/Developer/CoreSimulator/Devices//data/Containers/Data/Application//Documents/SingleViewCoreData.sqlite. Debido a que la ubicación de los datos de la aplicación cambia con cada versión de Xcode, la ruta anterior solo es válida para Xcode 7.

Abre la base de datos SQLite e inspecciona la tabla llamada ZPERSON. La tabla debe tener un registro, el que insertamos hace un minuto.

Contents of the SQLite DatabaseContents of the SQLite DatabaseContents of the SQLite Database

Debes tener en cuenta dos cosas. Primero, no es necesario comprender la estructura de la base de datos. Core Data gestiona la tienda de respaldo por nosotros y no necesitamos comprender su estructura para trabajar con Core Data. En segundo lugar, nunca accedas directamente a la tienda de respaldo. Core Data está a cargo de la tienda de respaldo y debemos respetar eso si queremos que Core Data haga bien su trabajo. Si comenzamos a interactuar con la base de datos SQLite, o cualquier otro tipo de tienda, no hay garantía de que Core Data continúe funcionando correctamente. En resumen, Core Data está a cargo de la tienda, así que déjalo en paz.

4. Obtención de registros

Aunque veremos de cerca NSFetchRequest en el próximo artículo, necesitamos que la clase NSFetchRequest solicite a Core Data información del gráfico de objetos que administra. Veamos cómo podemos recuperar el registro que insertamos anteriormente usando NSFetchRequest.

Después de inicializar la solicitud de recuperación, creamos un objeto NSEntityDescription y lo asignamos a la propiedad entity de la solicitud de recuperación. Como puedes ver, usamos la clase NSEntityDescription para decirle a Core Data en qué entidad estamos interesados.

La obtención de datos la gestiona la clase NSManagedObjectContext. Invocamos executeFetchRequest(_ :), pasando la solicitud de recuperación. Debido a que executeFetchRequest(_:) es un método de lanzamiento, envolvemos la llamada al método en una declaración do-catch.

El método devuelve una matriz de resultados si la solicitud de recuperación se realiza correctamente. Ten en cuenta que Core Data siempre devuelve una matriz si la solicitud de recuperación es exitosa, incluso si esperamos un resultado o si Core Data no encontró ningún registro coincidente.

Ejecuta la aplicación e inspecciona la salida en la consola de Xcode. A continuación puedes ver lo que se devolvió, una matriz con un objeto de tipo NSManagedObject. La entidad del objeto es Persona.

Para acceder a los atributos del registro, utilizamos la codificación de valores clave como lo hicimos anteriormente. Es importante familiarizarse con la codificación de valores clave si planeas trabajar con Core Data.

Es posible que te preguntes por qué registro el objeto person antes y después de registrar el nombre de la persona. Esta es en realidad una de las lecciones más importantes de este artículo. Revisa la siguiente salida.

La primera vez que registramos el objeto person en la consola, vemos: data: <fault>. Sin embargo, la segunda vez, los datos contienen el contenido de los atributos y relaciones del objeto. ¿Porqué? Esto tiene mucho que ver con las fallas, un concepto clave de Core Data.

5. Fallos

El concepto que subyace a las fallas no es exclusivo de Core Data. Si alguna vez has trabajado con Active Record en Ruby on Rails, lo siguiente sin duda te resultará familiar. El concepto no es idéntico, pero similar desde la perspectiva de un desarrollador.

Core Data intenta mantener su huella de memoria lo más baja posible y una de las estrategias que utiliza para lograr esto es fallando. Cuando obtuvimos los registros de la entidad Person hace un momento, Core Data ejecutó la solicitud de búsqueda, pero no inicializó completamente los objetos administrados que representan los registros obtenidos.

Lo que obtuvimos es una falla, un objeto de marcador de posición que representa el registro. El objeto es de tipo NSManagedObject y podemos tratarlo como tal. Al no inicializar completamente el registro, Core Data mantiene baja su huella de memoria. No es un ahorro de memoria significativo en nuestro ejemplo, pero imagina lo que sucedería si obtuviéramos docenas, cientos o incluso miles de registros.

Las fallas generalmente no son nada de lo que debas preocuparte. En el momento en que accedes a un atributo o relación de un objeto administrado, se desencadena el error, lo que significa que Core Data cambia el error en un objeto administrado realizado. Puedes ver esto en nuestro ejemplo y esa es también la razón por la que la segunda declaración de registro del objeto person no imprime una falla en la consola.

Fallar es algo que hace tropezar a muchos principiantes y, por lo tanto, quiero asegurarme de que comprendan los conceptos básicos de este concepto. Aprenderemos más sobre fallas más adelante en esta serie. Si quieres obtener más información sobre las fallas de Core Data, es posible que quieras leer este análisis en profundidad de las fallas en Core Data.

6. Actualización de registros

Actualizar registros es tan simple como crear un nuevo registro. Obtienes el registro, cambia un atributo o relación y guarda el contexto del objeto gestionado. Dado que el objeto administrado, el registro, está vinculado a un contexto de objeto administrado, este último es consciente de los cambios, inserciones, actualizaciones y eliminaciones. Cuando se guarda el contexto del objeto administrado, los datos principales lo propagan todo a la tienda de respaldo.

Revisa el siguiente bloque de código en el que actualizamos el registro que recuperamos cambiando la edad de la persona y guardando los cambios.

Puedes comprobar que la actualización se realizó correctamente echando otro vistazo a la tienda SQLite como hicimos anteriormente.

Updating a Record in the Backing StoreUpdating a Record in the Backing StoreUpdating a Record in the Backing Store

7. Eliminación de registros

La eliminación de un registro sigue el mismo patrón. Le indicamos al contexto del objeto administrado que un registro debe eliminarse del almacén persistente invocando deleteObject(_:) y pasar el objeto administrado que necesita ser eliminado.

En nuestro proyecto, elimina el objeto persona que obtuvimos anteriormente pasándolo al método deleteObject(_:) del contexto del objeto administrado. Ten en cuenta que la operación de eliminación no se confirma en la tienda de respaldo hasta que llamemos a save() en el contexto del objeto administrado.

Puedes comprobar que la operación de eliminación se realizó correctamente echando otro vistazo a la tienda SQLite.

Deleting a Record from the Backing StoreDeleting a Record from the Backing StoreDeleting a Record from the Backing Store

Conclusión

En este tutorial, incluimos mucho más que simplemente crear, capturar, actualizar y eliminar registros. Tocamos algunos conceptos importantes en los que se basan Core Data, como las fallas y la compatibilidad del modelo de datos.

En la próxima entrega de esta serie, aprenderás cómo crear y actualizar relaciones, y analizaremos en profundidad la clase NSFetchRequest. También empezaremos a usar NSPredicate y NSSortDescriptor para que nuestras solicitudes de captura sean flexibles, dinámicas y eficaces.

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.