Registro mágico, creado por Saul Mora, es una biblioteca de código abierto que hace que trabajar con Core Data sea más fácil y más elegante. La biblioteca se inspiró en el patrón de registro activo que también se encuentra en Ruby on Rails. Este tutorial te enseñará cómo usar Magical Record en tus propias aplicaciones!
Entonces, ¿por qué Registro Mágico? Si has estado desarrollando para iOS o OS X durante algún tiempo, es probable que hayas probado Core Data. Esto significa que sabe que puede ser un poco engorroso configurar una pila de Core Data y, para ser honesto, trabajar con Core Data puede ser un poco complejo, principalmente debido a su sintaxis detallada. Por ejemplo, obtener datos de un almacén persistente es bastante detallado, especialmente cuando se compara con cómo una aplicación de Ruby on Rails maneja esta tarea.
A pesar de que Magical Record hace que el trabajo con Core Data sea más fácil, es clave que tenga un buen conocimiento de Core Data si decide utilizarlo en un proyecto. A pesar de su nombre, Magical Record no realiza ninguna magia detrás de escena que haga que los Datos Core funcionen de manera diferente. En otras palabras, si tiene problemas en algún momento, es crucial que entienda cómo funciona Core Data internamente para poder solucionar cualquier problema que pueda surgir en el camino..
Desde la introducción de Magical Record, los requisitos se han incrementado a iOS 5.0 (o superior) u OS X 10.7 (o superior). También vale la pena mencionar que Magical Record admite ARC fuera de la caja..
La mejor manera de mostrarle lo que Magical Record tiene para ofrecer es crear una aplicación que haga uso de esta gran biblioteca. Le mostrará lo fácil que es comenzar con Magical Record y, desde el principio, le mostrará lo que implica crear un proyecto con Core Data y Magical Record. La aplicación que estamos a punto de crear es una aplicación para tomar notas en la que el usuario puede crear, actualizar y eliminar notas: un buen candidato para Core Data..
Ya que ha leído hasta aquí, asumo que está familiarizado con el desarrollo de iOS y que tiene una comprensión básica de los datos básicos. En este artículo, me centraré principalmente en el aspecto Datos básicos de la aplicación, lo que significa que no analizaré cada fragmento de código en detalle..
Comience por crear un nuevo proyecto basado en el Solicitud de vista única plantilla (figura 1) y nombrela Notas magicas (Figura 2). Establezca la familia de dispositivos en iPhone y habilitar ARC marcando la casilla marcada Utilice el conteo automático de referencias. No usaremos guiones gráficos o pruebas unitarias en este tutorial..
Ya que usaremos Datos Básicos en este proyecto, no olvide vincular su proyecto con el marco de Datos Básicos. Dado que este es un tutorial más avanzado, asumo que ya sabes cómo hacerlo.
Agregar la biblioteca de Registro Mágico a tu proyecto no requiere ninguna magia. Descargue la última versión de GitHub, abra el archivo y arrastre la carpeta llamada Registro magico en su proyecto Xcode. Asegúrese de copiar también el contenido de la carpeta en su proyecto marcando la casilla de verificación etiquetada Copie los elementos en la carpeta del grupo de destino (si es necesario) y no olvide agregar la biblioteca de Registro Mágico al destino de Notas Mágicas (figura 3). Un enfoque alternativo para agregar Magical Record a su proyecto es usar CocoaPods.
Para utilizar Magical Record en tus clases, necesitamos importar un archivo de encabezado, CoreData + MagicalRecord.h
. Sin embargo, dado que usaremos Magical Record bastante en este tutorial, es mucho más conveniente mover esta declaración de importación a la de su proyecto. Prefijo.pch archivo en su lugar. Esto asegurará que Magical Record esté disponible en cada clase de su proyecto.
Por defecto, todos los métodos de Registro Mágico tienen el prefijo SEÑOR_. Puedes omitir el SEÑOR_ prefijo agregando una línea adicional a su proyecto Prefijo.pch expediente, #define MR_SHORTHAND
. Es importante que agregue esta línea antes de importar el archivo de encabezado del registro mágico.
// // Encabezado de prefijo para todos los archivos de origen del destino 'Notas mágicas' en el proyecto 'Notas mágicas' // #import#ifndef __IPHONE_4_0 #warning "Este proyecto usa funciones solo disponibles en iOS SDK 4.0 y posteriores". #endif #ifdef __OBJC__ #import #importar #define MR_SHORTHAND #import "CoreData + MagicalRecord.h" #endif
Antes de configurar la pila de Core Data, necesitamos crear un modelo de Core Data. El modelo de datos básicos para este proyecto es simple, ya que consta de una sola entidad denominada Nota. los Nota la entidad tiene cuatro atributos, fecha, título, cuerpo, y palabras clave. Título, cuerpo y palabras clave son de tipo cuerda, mientras que la fecha es de tipo fecha.
Comience creando un nuevo modelo de Core Data y asígnele un nombre Notas mágicas (Figura 4). Crear el Nota Entidad y agregue los cuatro atributos como se describe arriba (figura 5).
Antes de continuar, necesitamos crear un diseño personalizado. NSManagedObject
subclase para el Nota
entidad. Esto es importante ya que Magical Record agrega una serie de métodos de clase útiles al NSManagedObject
clase, lo que hará trabajar con el Nota Entidad mucho más fácil como verás en unos minutos. Selecciona el Nota En su modelo de datos básicos, cree un nuevo archivo, seleccione la Datos básicos pestaña a la izquierda, y elija la NSManagedObject subclass Opción a la derecha (figura 6).
Configurar una pila de Core Data es bastante trabajo si no usas una de las plantillas de Xcode provistas. Con Magical Record, sin embargo, este no es el caso. Dirígete a la aplicación: didFinishLaunchingWithOptions:
Método del delegado de su aplicación y agregue el siguiente fragmento de código.
[MagicalRecord setupCoreDataStack];
Eso es todo al respecto. De forma predeterminada, el nombre de la tienda que Magical Record crea para usted es idéntico al nombre de su aplicación. Sin embargo, puede personalizar el nombre de la tienda invocando setupCoreDataStackWithStoreNamed:
En cambio y pasando el nombre de la tienda..
Detrás de las escenas, Magical Record creará una instancia de un contexto de objeto gestionado en el hilo principal, así como un coordinador de tienda persistente y un modelo de objeto gestionado. Que mágico es eso?
Explotación florestal: La capacidad de registrar mensajes y errores de Core Data en la consola está integrada en Magical Record. Eche un vistazo a la consola después de construir y ejecutar su aplicación por primera vez. Los registros en la consola le muestran exactamente lo que Magical Record está haciendo entre bastidores.
Antes de que podamos comenzar a crear nuevas notas, primero necesitamos un trabajo duro. Revise su solicitud de delegado aplicación: didFinishLaunchingWithOptions:
Método e inicialice un controlador de navegación con el controlador de vista principal como su controlador de vista raíz. Eche un vistazo a la implementación completa de aplicación: didFinishLaunchingWithOptions:
.
- Aplicación (BOOL): (UIApplication *) aplicación didFinishLaunchingWithOptions: (NSDictionary *) launchOptions // Setup Core Data Stack [MagicalRecord setupCoreDataStack]; // Inicializar View Controller self.viewController = [[MTViewController alloc] initWithNibName: @ paquete "MTViewController": nil]; // Inicializar el controlador de navegación UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController: self.viewController]; // Inicializar la ventana self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] lines]]; [self.window setRootViewController: nc]; [self.window makeKeyAndVisible]; devuelve SÍ;
Mostraremos las notas en una vista de tabla, así que comience agregando una salida para una vista de tabla en el archivo de encabezado del controlador de vista principal. Seleccione el archivo XIB del controlador de vista principal y arrastre un UITableView
instancia en la vista del controlador de vista. No te olvides de asignar la Propietario del archivo como la vista de la mesa fuente de datos
y delegar
. Además, asegúrese de conectar el Propietario del archivo Vista de la tabla de salida con la vista de tabla que acabamos de agregar a su vista.
#importar@interface MTViewController: UIViewController @property (no atómico, débil) IBOutlet UITableView * tableView; @fin
En el archivo de implementación del controlador de vista principal, agregue una propiedad privada llamada notas a la extensión de la clase en la parte superior. Asegúrese de que la propiedad es de tipo NSMutableArray
. los notas
La propiedad almacenará las notas que obtenemos del almacén de datos y servirá como fuente de datos de la vista de tabla..
#import "MTViewController.h" @interface MTViewController () @property (nonatomic, strong) notas de NSMutableArray *; @fin
En el controlador de vista viewDidLoad
Método, configuramos la vista llamando setupView
en el controlador de vista principal. Esto no es más que un método de ayuda para mantener el viewDidLoad
Método conciso y ordenado. En setupView
, agregamos un botón de edición y adición a la barra de navegación y recuperamos las notas del almacén de datos invocando fetchNotes
método.
- (void) viewDidLoad [super viewDidLoad]; // Vista de configuración [auto configuraciónView];
- (void) setupView // Crear botón de edición UIBarButtonItem * editButton = [[UIBarButtonItem alloc] initWithTitle: @ "Edit" style: UIBarButtonItemStyleBordered target: self action: @selector (editNotes :)]; self.navigationItem.leftBarButtonItem = editButton; // Crear botón Agregar UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithTitle: @ style "Add": UIBarButtonItemStyleBordered target: self action: @selector (addNote :)]; self.navigationItem.rightBarButtonItem = addButton; // Fetch Notas [self fetchNotes];
Podría sorprenderte que el fetchNotes
El método es solo una línea de código. Todo esto es gracias a Magical Record. Obtener las notas del almacén de datos es tan simple como llamar encuentra todos
sobre el Nota
clase. El método devuelve una matriz de registros como cabría esperar. No olvides importar el archivo de cabecera de la Nota
clase en la parte superior del archivo de implementación del controlador de vista principal.
- (void) fetchNotes // Fetch Notes self.notes = [NSMutableArray arrayWithArray: [Note findAll]];
La clasificación de los registros es fácil y elegante. Olvide los descriptores de clasificación y eche un vistazo a la implementación actualizada de fetchNotes
método a continuación.
- (void) fetchNotes // Fetch Notes self.notes = [NSMutableArray arrayWithArray: [Nota findAllSortedBy: @ "date" ascendente: YES]];
los editarNotas:
El método es sencillo. Todo lo que hacemos es cambiar el estilo de edición de la vista de tabla. Eso debería ser suficiente por ahora.
- (void) editNotes: (id) sender [self.tableView setEditing:! [self.tableView isEditing] animated: YES];
los añadir la nota:
El método permanece en blanco por el momento.
- (void) addNote: (id) sender
Antes de construir y ejecutar su aplicación, necesitamos implementar los métodos requeridos del protocolo de origen de datos de vista de tabla. Si está familiarizado con el desarrollo de iOS, esto no debería ser demasiado difícil. Eche un vistazo a las implementaciones de los diversos métodos a continuación para aclararlas..
- (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) sección return [self.notes count];
- (UITableViewCell *) tableView: (UITableView *) aTableView cellForRowAtIndexPath: (NSIndexPath *) indexPath static NSString * CellIdentifier = @ "Cell"; UITableViewCell * cell = [aTableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (cell == nil) cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: CellIdentifier]; // Configurar celda [cell setAccessoryType: UITableViewCellAccessoryDisclosureIndicator]; // Fetch Note Note * note = [self.notes objectAtIndex: [indexPath row]]; // Configurar celda [cell.textLabel setText: [título de la nota]]; [cell.detailTextLabel setText: [observe las palabras clave]]; celda de retorno
- (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath return YES;
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath if (editingStyle == UITableViewCellEditingStyleDelete)
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath [tableView deselectRowAtIndexPath: indexPath animated: YES];
Construye y ejecuta tu aplicación.
Cuando el usuario toca el botón Agregar, debería aparecer una vista modal que le permita agregar una nueva nota al almacén de datos. Crear un nuevo UIViewController
subclase y nombre MTEditNoteViewController
. Como su nombre lo indica, también usaremos este controlador de vista para editar notas..
Antes de dirigirse al archivo XIB del controlador de vista, agregue tres salidas al archivo de encabezado del controlador de vista. Los dos primeros puntos de venta son instancias de UITextField
Para el título y las palabras clave de la nota. La tercera salida es una instancia de UITextView
para el cuerpo de la nota.
#importar@interface MTEditNoteViewController: UIViewController @property (no atómico, débil) IBOutlet UITextField * titleField; @ propiedad (no atómica, débil) IBOutlet UITextField * keywordsField; @ propiedad (no atómica, débil) IBOutlet UITextView * bodyView; @fin
Crear la interfaz de usuario del controlador de vista de edición de notas no debería ser un gran desafío. Añadir dos UITextField
instancias y una UITextView
instancia a la vista del controlador de vista. Configure los campos de texto como desee, por ejemplo, dándoles un texto de marcador de posición. No te olvides de conectar el Propietario del archivoSalidas con los campos de texto y la vista de texto..
Ya que estaremos usando el MTEditNoteViewController
Para agregar y editar notas, es importante que sepamos en qué estado (agregando o editando) se encuentra el controlador de vista en cualquier momento. Hay varias formas de resolver este problema. Una forma es agregar un privado Nota
propiedad del controlador de vista, que es nulo
si se crea una nueva nota y se configura durante la inicialización cuando se edita una nota. En situaciones como esta, prefiero trabajar con inicializadores especializados para evitar confusiones y esto es también lo que me permite mantener el Nota
Propiedad privada. Además de lo privado. Nota
propiedad, también agregamos una segunda propiedad privada llamada isEditing
, un booleano La razón de esto se aclarará en unos minutos. Además, no te olvides de importar el archivo de encabezado de la Nota
clase.
#import "MTEditNoteViewController.h" #import "Note.h" @interface MTEditNoteViewController () @property (nonatomic, strong) Note * note; @ propiedad (no atómica, asignar) BOOL isEditing; @fin
Vamos a ver los distintos métodos paso a paso. Primero, queremos asegurarnos de que podemos agregar nuevas notas al almacén de datos sin problemas. Comenzamos con el initWithNibName: paquete:
método. El único cambio que hacemos es establecer la isEditing
propiedad a NO
.
- (id) initWithNibName: (NSString *) paquete nibNameOrNil: (NSBundle *) nibBundleOrNil self = [super initWithNibName: nibNameOrNil paquete: nibBundleOrNil]; if (self) // Establecer marca self.isEditing = NO; devuélvete a ti mismo;
Como vimos anteriormente, en el viewDidLoad
Método, configuramos la vista invocando un setupView
Método en el que creamos los botones cancelar y guardar. Tenga en cuenta que solo creamos el botón de cancelar si el Nota
propiedad es igual a nulo
. La razón es que presentamos el controlador de vista de manera modal cuando agregamos nuevas notas, pero presionamos el controlador de vista en una pila de navegación cuando editamos una nota. Si el Nota
la propiedad no es igual a nulo
, También rellenamos los campos de texto y la vista de texto con el contenido de la Nota
propiedad.
- (void) viewDidLoad [super viewDidLoad]; // Vista de configuración [auto configuraciónView];
- (void) setupView // Create Cancel Button if (! self.note) UIBarButtonItem * cancelButton = [[UIBarButtonItem alloc] initWithTitle: @ "Cancel" style: UIBarButtonItemStyleBordered target: auto action: @selector (cancelar :) self.navigationItem.leftBarButtonItem = cancelButton; // Crear botón Guardar UIBarButtonItem * saveButton = [[UIBarButtonItem alloc] initWithTitle: @ estilo "Guardar": UIBarButtonItemStyleBordered target: self action: @selector (save :)]; self.navigationItem.rightBarButtonItem = saveButton; if (self.note) // Rellenar campos de formulario [self.titleField setText: [self.note title]]; [self.keywordsField setText: [palabras clave self.note]]; [self.bodyView setText: [self.note body]];
los cancelar:
El método no debería contener sorpresas..
- (nulo) cancelar: (id) remitente // Descartar controlador de vista [self dismissViewControllerAnimated: YES completed: nil];
los salvar:
El método es un poco más detallado, pero tampoco debería ser demasiado difícil. Primero verificamos si el controlador de vista Nota
Se establece la propiedad. Si está configurado, entonces sabemos que una nota se está editando, no se crea. Si el Nota
propiedad es igual a nulo
entonces sabemos que se debe crear una nueva nota. Descartar el controlador de vista es un poco complicado, ya que debemos descartar el controlador de vista si se presenta de manera modal cuando se creó una nota y sacarlo de la pila de navegación cuando se editó una nota. Esa es la razón por la que creamos el isEditing
propiedad.
- (void) save: (id) sender if (! self.note) // Create Note self.note = [Note createEntity]; // Configurar nota [self.note setDate: [NSDate date]]; // Configurar nota [self.note setTitle: [self.titleField text]]; [self.note setKeywords: [self.keywordsField text]]; [self.note setBody: [self.bodyView text]]; // Guardar contexto de objeto gestionado [[NSManagedObjectContext defaultContext] saveNestedContexts]; if (self.isEditing) // Pop View Controller de la pila de navegación [self.navigationController popViewControllerAnimated: YES]; else // Descartar controlador de vista [self dismissViewControllerAnimated: YES completed: nil];
Como puede ver, la creación de una nueva nota es otra de una sola línea cuando se usa Magical Record. Rellenamos la nota con el contenido de los campos del formulario y guardamos el contexto del objeto administrado de la nota. Recuperar una referencia al contexto del objeto gestionado es fácil con Magical Record. Todo lo que necesitamos hacer es preguntar al NSManagedObjectContext
clase para el contexto por defecto. Guardar el contexto es idéntico a guardar un contexto sin registro mágico. A pesar de que registramos el error si algo sale mal, esto no es realmente necesario ya que Magical Record registrará cualquier error en la consola para nosotros.
Ahora es el momento de enmendar el añadir la nota:
Método en el controlador de vista principal. No te olvides de importar el archivo de cabecera de la MTEditNoteViewController
clase.
- (void) addNote: (id) sender // Initialize Edit Note View Controller MTEditNoteViewController * vc = [[MTEditNoteViewController alloc] initWithNibName: @ "MTEditNoteViewController" bundle: [NSBundle mainBundle]]; // Inicializar el controlador de navegación UINavigationController * nc = [[UINavigationController alloc] initWithRootViewController: vc]; // Controlador de vista actual [self.navigationController presentViewController: nc animated: YES complete: nil];
Cada vez que se agrega una nueva nota al almacén de datos, debemos actualizar la vista de tabla para mostrar los cambios. Para una aplicación de producción, un mejor enfoque sería observar los cambios en el contexto del objeto gestionado. Sin embargo, en esta aplicación de ejemplo, obtenemos las notas del almacén de datos y volvemos a cargar la vista de tabla cada vez que aparece la vista principal (re). Esto es costoso y, por lo tanto, no se recomienda si planea enviar su solicitud a la App Store. Para situaciones como esta, un controlador de resultados es una solución perfecta.
- (void) viewWillAppear: (BOOL) animated [super viewWillAppear: animated]; // Fetch Notas [self fetchNotes]; // Reload Table View [self.tableView reloadData];
Actualizar notas es casi tan fácil como agregar notas. Como mencioné anteriormente, crearemos un inicializador especializado para configurar el controlador de vista Nota
propiedad. Actualice el archivo de encabezado de la MTEditNoteViewController
clase agregando el nuevo inicializador como se muestra a continuación. No olvide agregar también una declaración de clase hacia adelante para el Nota
clase al archivo de cabecera.
#importar@class Note; @interface MTEditNoteViewController: UIViewController @property (no atómico, débil) IBOutlet UITextField * titleField; @ propiedad (no atómica, débil) IBOutlet UITextField * keywordsField; @ propiedad (no atómica, débil) IBOutlet UITextView * bodyView; - (id) initWithNote: (Nota *) nota; @fin
El inicializador especializado no es realmente especial. Todo lo que hacemos es configurar el controlador de vista. Nota
y isEditing
propiedades como puedes ver abajo.
- (id) initWithNote: (Nota *) nota self = [self initWithNibName: @ paquete "MTEditNoteViewController": [NSBundle mainBundle]]; if (self) // Establecer nota self.note = note; // Establecer la marca self.isEditing = YES; devuélvete a ti mismo;
Antes de compilar y ejecutar la aplicación una vez más, debemos actualizar el controlador de vista principal tableView: didSelectRowAtIndexPath:
método. En este método, obtenemos la nota correcta, inicializamos una instancia del MTEditNoteViewController
clase, y empuje el controlador de vista en la pila de navegación.
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath [tableView deselectRowAtIndexPath: indexPath animated: YES]; // Fetch Nota Nota * nota = [self.notes objectAtIndex: [indexPath row]]; // Inicializar Editar nota Ver controlador MTEditNoteViewController * vc = [[MTEditNoteViewController alloc] initWithNote: note]; // Inserte el controlador de vista en la pila de navegación [self.navigationController pushViewController: vc animated: YES];
Para borrar nota, necesitamos enmendar el tableView: commitEditingStyle: forRowAtIndexPath:
método. Recuperamos la nota, la eliminamos del origen de datos y del contexto del objeto gestionado, y actualizamos la vista de tabla. Como puede ver, eliminar un registro o entidad del almacén de datos es tan simple como enviarle un mensaje de deleteEntity
.
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath * (índice) Indicación de la tabla de comandos de la tabla de instrumentos del estado. ]; // Eliminar nota del origen de datos [self.notes removeObjectAtIndex: [indexPath row]]; // Eliminar entidad [note deleteEntity]; // Actualizar vista de tabla [tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationFade];
Al construir Registro Mágico, solo hemos arañado la superficie. Quiero enfatizar que Magical Record es una biblioteca robusta y madura y no solo una amalgama de un puñado de categorías útiles. Como les mostré, Magical Record hace que trabajar con Core Data sea mucho más fácil y menos detallado. Las tareas comunes a menudo son de una sola línea. Compare los siguientes fragmentos de código para obtener todas las notas y ordenarlas por fecha. Usando Core Data esto resultaría en el siguiente fragmento de código.
NSFetchRequest * fr = [[NSFetchRequest alloc] init]; NSEntityDescription * ed = [NSEntityDescription entityForName: @ "Note" enManagedObjectContext: [NSManagedObjectContext defaultContext]]; [fr setEntity: ed]; NSSortDescriptor * sd = [NSSortDescriptor sortDescriptorWithKey: @ "date" ascendente: YES]; [fr setSortDescriptors: @ [sd]]; NSError * error = nil; NSArray * result = [[NSManagedObjectContext defaultContext] executeFetchRequest: fr error: & error];
Usando Magical Record, sin embargo, esto solo requiere una línea de código.
NSArray * result = [Nota findAllSortedBy: @ "date" ascendente: YES];
Si tuviéramos que agregar la capacidad de buscar en la lista de notas, un enfoque, aunque no es ideal, sería buscar en el almacén de datos todas las notas con un título o palabra clave que contenga la consulta. Usando Magical Record esto resultaría en la siguiente implementación.
NSPredicate * predicate1 = [NSPredicate predicateWithFormat: @ "title contiene [cd]% @", consulta]; NSPredicate * predicate2 = [NSPredicate predicateWithFormat: @ "keywords contiene [cd]% @", consulta]; NSPredicate * predicate = [NSCompoundPredicate orPredicateWithSubpredicates: @ [predicate1, predicate2]]; NSArray * result = [Note findAllWithPredicate: predicate];
Como dije, Magical Record tiene mucho más que ofrecer que lo que te he mostrado en este tutorial. Desde la versión 2.0, Magical Record puede tratar con contextos anidados y también proporciona soporte para iCloud y operaciones de subprocesos. El objetivo principal de este tutorial es mostrarle que los Datos Básicos no tienen que ser complicados y Saul Mora lo ilustra con Magical Record.