En este tercer artículo sobre mi aprendizaje de Swift, vuelvo a hablar de Core Data pero esta vez me centraré en NSFetchRequest o cómo recuperar la información almacenada en mi base de datos.
Cuatro métodos de utilizar NSFetchRequest
Existen cuatro, sí, cuatro métodos de utilizar NSFetchRequest. Yo también me sorprendí al descubrirlo.
- Método 1:
123456// Creo una Fetch Request con el constructor sin parámetros.let fetchRequest = NSFetchRequest()// Creo una entidad "Persona"let entity = NSEntityDescription.entityForName("Persona", inManagedObjectContext: managedContext)// Asigno la entidad a mi Fetch Request.fetchRequest.entity = entityCreo una instancia de NSFetchRequest y una instancia de la entidad que quiero utilizar. Después asigno esa entidad a la instancia de NSFetchRequest. El segundo método es mucho más sencillo y por eso será seguramente el que más utilices.
- Método 2:
1let fetchRequest = NSFetchRequest(entityName: "Persona")
En una simple línea hago exactamente lo mismo que el primer método. Utilizo un constructor que me permite pasarle el nombre de la entidad que quiero utilizar.
- Método 3:
1let fetchRequest = appDelegate.managedObjectModel.fetchRequestFromTemplateWithName("PersonaFetchRequest")
Al definir tus entidades, habrás visto quizás que puedes también definir Fetch Request. Si utilizar a menudo una Fetch Request, te puede ser útil definirla directamente en tu modelo de datos en vez de estar escribiéndola. El siguiente método te permite además personalizar un poco esa Fetch Request sin tener que pelearte con NSPredicate del que hablaré más adelante.
- Método 4:
1let fetchRequest4 = appDelegate.managedObjectModel.fetchRequestFromTemplateWithName("PersonaFetchRequest", substitutionVariables: ["NOMBRE" : "Manolo"])
Es lo mismo que antes pero filtrando por el nombre «Manolo» que sustituirá la cadena «NOMBRE». Esto puede ser útil en algunos casos pero tiene una gran desventaja: No puedes ordenar los resultados. La verdad es que los creadores de este método de hacer Fetch Request se podrían haber estirado un poco y permitir añadir una ordenación, pero no es posible.
De todas formas, realmente este método es útil si vas a utilizar la misma Fetch Request en distintos lugares de tu aplicación. Si no es así, no es que te vayas ahorrar mucho trabajo utilizándolo. A día de hoy, no me he encontrado con ningún caso en el que lo acabara utilizando.
También hay cuatro tipos de resultados de NSFetchRequest
Una Fetch Request puede devolver cuatro tipos diferentes de resultados. A la hora de la verdad, utilizarás principalmente dos de ellos, pero es bueno conocer todas las posibilidades que te ofrece Core Data.
NSManagedObjectResultType
Este es el tipo de resultado por defecto. Devuelve NSManagedObject por defecto.
NSCountResultType
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func devolverNumeroPersonas() { // Creo la Fetch Request. let fetchRequest = NSFetchRequest(entityName: "Persona") // Asigno el tipo .CountResultType fetchRequest.resultType = .CountResultType do { let results = try managedContext.executeFetchRequest(fetchRequest) // El resultado es un array con un número entero. let numeroPersonas = results.first!.integerValue print("numeroPersonas => \(numeroPersonas)") } catch let error as NSError { print("Error al recuperar el número de Personas \(error)") } } |
Este será el tipo de resultado que más utilices después del NSManagedObjectResultType. Devuelve un número entero, el número de objetos que coinciden con tu Fetch Request. El equivalente de “Count()” en MySQL.
Existe otra manera de sacar el número de objetos. Puedes utilizar el método «countForFetchRequest».
1 2 3 4 5 6 7 8 9 10 11 12 |
func devolverNumeroPersonas() { // Creo la Fetch Request. let fetchRequest = NSFetchRequest(entityName: "Persona") var error: NSError? let results = managedContext.countForFetchRequest(fetchRequest, error: &error) if error == nil { print("numeroPersonas => \(results)") } else { print("Error al recuperar el número de Personas \(error)") } } |
La única diferencia entre estos dos métodos es que el primero devuelve un array con un entero y el segundo método devuelve directamente un entero. Comprobando el log de SQLLite que puedes activar añadiendo este argumento “-com.apple.CoreData.SQLDebug 3”, se puede ver que las consultas realizadas son exactamente iguales.
CoreData: sql: SELECT COUNT(*) FROM ZPERSONA
NSDictionaryResultType
Este tipo de resultado se utiliza con funciones de SQL que realizan cálculos sobre los datos. Estos cálculos pueden ser suma, mínimo, máximo, media, … Te sonarán seguro de utilizarlos en MySQL. Puedes consultar las funciones existentes en la página de apple sobre NSExpression. Te recomiendo también este artículo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
func buscarEdadMedia() { // Creo la Fetch Request. let fetchRequest = NSFetchRequest(entityName: "Persona") // Asigno el tipo .DictionaryResultType fetchRequest.resultType = .DictionaryResultType // Creo la expresión para calcular la media usando "average:" let avgExpressionDesc = NSExpressionDescription() // Nombre. avgExpressionDesc.name = "edadMedia" // Expresión y a qué campo se aplica. avgExpressionDesc.expression = NSExpression(forFunction: "average:", arguments: [NSExpression(forKeyPath: "edad")]) // Tipo del dato devuelto: Float. avgExpressionDesc.expressionResultType = .FloatAttributeType // Propiedades a devolver. fetchRequest.propertiesToFetch = [avgExpressionDesc] do { let results = try managedContext.executeFetchRequest(fetchRequest) as! [NSDictionary] let resultDict = results.first! print("La edad media de las personas es => \(resultDict["edadMedia"]!)") } catch let error as NSError { print("Error al recuperar la edad media de Personas \(error)") } } |
En este ejemplo, estoy buscando la edad media de las personas de mi base de datos.
NSManagedObjectIdResultType
Este tipo de resultado devuelve un array de id de objetos en lugar de los objetos. Es como si solicitáramos en MySQL las claves primarias en lugar de todas las filas. Este tipo de resultado ya no se utiliza prácticamente hoy en día así que no me voy a parar a hablar de él.
NSPredicate
No es posible escribir sobre Fetch Request sin nombrar NSPredicate. NSPredicate no es exclusivo de Core Data. De hecho, forma parte de Foundation. Puedes utilizar predicados para filtrar NSArray, NSSet o NSDictionary.
Es bastante sencillo de utilizar y te recordará a un WHERE de MySQL.
Por ejemplo, si quiero buscar las personas de mi base de datos que tienen más de 25 años.
1 2 |
let fetchRequest = NSFetchRequest(entityName: "Persona") fetchRequest.predicate = NSPredicate(format: "edad > 25") |
Puedes concatenar más de una condición utilizando como no AND u OR.
1 2 |
let fetchRequest = NSFetchRequest(entityName: "Persona") fetchRequest.predicate = NSPredicate(format: "edad > 25 AND edad < 40") |
También existe la posibilidad que quieras utilizar NSCompoundPredicate o NSComparisonPredicate. No hablaré de esto aquí, veo suficiente de momento concatenar utilizando las palabras AND u OR.
Puedes también sustituir variables dentro de NSPredicate:
1 2 |
let fetchRequest = NSFetchRequest(entityName: "Persona") fetchRequest.predicate = NSPredicate(format: "edad > %@", "25") |
Como puedes ver, es muy sencillo de utilizar.
NSSortDescriptor
Lo último que me queda por enseñarte es cómo ordenar el listado de resultados. De nuevo, NSSortDescriptor es parte de Foundation. Esto es algo que muchas veces se me olvida porque se podría utilizar para ordenar NSArray por ejemplo de forma sencilla.
1 2 |
let fetchRequest = NSFetchRequest(entityName: "Coche") fetchRequest.sortDescriptors = [NSSortDescriptor(key: "ano_compra", ascending: true)] |
En este ejemplo, saco el listado de coches ordenando por el año de compra. Como puedes ver, es posible ordenar por varios campos al mismo tiempo ya que se debe pasar un array de NSSortDescriptor.
Ya estás listo para utilizar Fetch Request
Ya sabes lo suficiente para utilizar NSFetchRequest y hacer prácticamente todo lo que se te puede ocurrir con ello. Sin duda, existen más temas relacionados con Core Data tales como NSAsynchronousFetchRequest que te permite hacer Fetch Request costosas sin bloquear el dispositivo de tu usuario realizándolas de forma asíncrona o por ejemplo NSFetchedResultsController que te permite asociar un Table View a tu fetch request de forma muy sencilla. Todo esto es material suficiente para otro artículo o más de uno.