Saltar a contenido

Ejercicios de modelos de datos

En esta sesión vamos a continuar trabajando sobre la aplicación de notas de la primera sesión de Core Data. Necesitarás que la sesión anterior esté terminada para poder continuar con esta.

Vamos a ampliar el modelo de datos añadiendo una nueva entidad, "Libreta", que tendrá una relación de uno a muchos con "Nota". Cada libreta contendrá muchas notas, pero cada nota solo puede estar en una libreta.

Añadir la entidad Libreta (0,25 puntos)

  • Añadir la entidad Libreta, con un atributo llamado nombre de tipo String
  • Crear una relación "a muchos" de Libreta a Nota y "a uno" de Nota a Libreta (la inversa)

Recuerda que según las convenciones de Core Data, la relación de Libreta a Nota se debería llamar "notas" y la de nota a libreta, "libreta". Así, por ejemplo luego podríamos acceder a las notas de una libreta miLibreta con miLibreta.notas, lo que resulta intuitivo en el código.

Crear libretas (0,25 puntos)

Para simplificar será un botón "nueva libreta" en la pantalla inicial, que al pulsarlo debe mostrar un alert con un campo de texto para escribir el nombre. Tendrás que definirte el action correspondiente para detectar que se ha pulsado el botón. En el action puedes llamar al siguiente código para mostrar el alert:

func nuevaLibreta() {
    let alert = UIAlertController(title: "Nueva libreta",
                                  message: "Escribe el nombre para la nueva libreta",
                                  preferredStyle: .alert)
    let crear = UIAlertAction(title: "Crear", style: .default) {
        action in
        let nombre = alert.textFields![0].text!
        //AQUI FALTA GUARDAR LA LIBRETA CON CORE DATA
    }
    let cancelar = UIAlertAction(title: "Cancelar", style: .cancel) {
        action in
    }
    alert.addAction(crear)
    alert.addAction(cancelar)
    alert.addTextField() { $0.placeholder = "Nombre"}
    self.present(alert, animated: true)
}
  • En el código anterior falta guardar la libreta usando Core Data, implementa esta funcionalidad

Listar libretas (0,25 puntos)

Antes de empezar con esto asegúrate de que las libretas se están creando correctamente en la base de datos. En el viewWillAppear de la pantalla de lista de notas puedes poner provisionalmente una consulta que las imprima en la consola (luego acuérdate de quitarlo)

override func viewWillAppear(_ animated: Bool) {
    let queryLibretas = NSFetchRequest<Libreta>(entityName:"Libreta")
    guard let miDelegate = UIApplication.shared.delegate as? AppDelegate else {
        return
    }
    let miContexto = miDelegate.persistentContainer.viewContext
    let libretas = try! miContexto.fetch(queryLibretas)
    for libreta in libretas {
        print(libreta.nombre)
    }
}

Para que el usuario pueda seleccionar la libreta en la pantalla de creación de notas vamos a usar un picker view, la típica "rueda" para seleccionar valores de una lista.

  • Añade un componente picker view a la pantalla de creación de notas. Crea un outlet para poder acceder a él desde Swift. Dale el nombre que quieras.
  • Desde el punto de vista del código, un picker view requiere de un delegate y de un datasource (más o menos como las tablas). Crea una nueva clase GestorPicker en un archivo del mismo nombre, así no llenamos de tanto código el ViewController
import Foundation
import UIKit
import CoreData


class GestorPicker : NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
    var libretas = [Libreta]()

    //devuelve el número de columnas del picker. En nuestro caso solo 1
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    //devuelve el número de filas del picker (== número de libretas)
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return self.libretas.count
    }

    //devuelve el título de una fila determinada
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return self.libretas[row].nombre
    }

    //para cargar la lista de libretas desde Core Data
    func cargarLista() {
        let miDelegate = UIApplication.shared.delegate as! AppDelegate
        let miContexto = miDelegate.persistentContainer.viewContext
        let request = NSFetchRequest<Libreta>(entityName:"Libreta")
        self.libretas = try! miContexto.fetch(request)
    }
}

Ahora en el ViewController añade una nueva propiedad , una instancia de la clase anterior

//Esto es una propiedad del ViewController, va dentro de la clase y FUERA de los métodos
let miGestorPicker = GestorPicker()

y en el viewDidLoad del ViewController conectamos el picker con su delegate/datasource y cargamos la lista de libretas con Core Data

//Aquí, "picker" es el outlet que representa al "picker view"
//CAMBIALO por el nombre que le hayas dado
self.picker.delegate = self.miGestorPicker
self.picker.dataSource = self.miGestorPicker
//cargamos las libretas con Core Data
self.miGestorPicker.cargarLista()

Tal como está ahora la aplicación la lista de libretas debe aparecer cuando se arranca, pero si se añade una nueva libreta no aparecerá en el picker hasta que se pare la app y se vuelva a arrancar (que es cuando se llama a cargarLista()). Puedes arreglarlo haciendo dos cosas justo después de guardar la nueva libreta con core data (en la función nuevaLibreta de antes):

  • Añadir la nueva libreta a la lista que tiene dentro el miGestorPicker:
self.miGestorPicker.libretas.append(libreta)
  • Decirle al componente picker view que se repinte recargando los datos
//de nuevo, "picker" es el outlet del "picker view". CÁMBIALO POR EL TUYO
self.picker.reloadAllComponents()

Asociar una libreta a la nota actual (0,25 puntos)

Falta que cuando se guarde la nota, se le asocie la libreta que aparece seleccionada en el picker view. Igual que asignas el "texto" y la "fecha" de la nota, asígnale también el objeto libreta usando la relación "a uno" entre Nota y Libreta. Para saber qué número de opción está seleccionado actualmente en un picker puedes usar su método selectedRow:

//de nuevo, "picker" es el outlet del "picker view". CÁMBIALO POR EL TUYO
let numLibreta = self.picker.selectedRow(inComponent: 0)

El objeto Libreta en esa posición será la misma posición del array libretas del self.miGestorPicker.

Mostrar la libreta al listar las notas

Puedes mostrar el nombre de la libreta en la parte del texto de detalle de la celda (es la propiedad detailTextLabel de la celda). Primero tendrás que cambiar el tipo de celda de Basic a Left detail o Right detail, según donde quieras que aparezca el nombre.