Saltar al contenido →

@FetchRequest Tutorial

La aparición de SwiftUI junto con Combine ha supuesto un salto en lo que ha desarrollo de interfaces de usuario se refiere dentro del entorno Apple.

Una de las mejoras que presenta SwiftUI es @FetchRequest, un property wrapper que recibiendo un NSFetchRequest se encarga de mantener sincronizada la vista con los datos que arroja dicha petición a base de datos.

Y lo mejor es que se hace con muy pocas líneas código.

Preparativos

Ante de nada tendremos que realizar una serie de preparativos previos, que son…

  1. Crear nuestro stack de Core Data
  2. Asignar nuestro NSManagedObjectContext al del Environment de SwiftUI

Los elementos que van a estar involucrados son…

  1. Un NSFetchRequest
  2. El @FetchRequest que manejará el fetch anterior
  3. Una vista que presente los datos del NSFetchRequest

Los pasos Previos

La creación del stack de Core Data ha traído pocos cambios con respecto a la versión anterior, pero aunque sean pocos son bastantes importantes.

Uno de ellos, quizá el principal, es la incorporación de NSPersistentCloudKitContainer, un nuevo contenedor de persistencia que permite la sincronización automática de nuestro modelo con CloudKit, y por extensión nuestra app ya sincroniza el modelo de datos con todos los dispositovos en los que la tengamos instalada y estemos identificados con el mismo usuario de iCloud.

Como el motivo de este artículo es otro no vamos a ver el uso y configuración de este tipo de contenedor.

La asignación de nuestro managed context al del variable contenida en Environment la hacemos en el SceneDelegate, concretamente en la vista superior de la jerarquía mediante la llamada a la función environment(_ keyPath: , _ value:) de la vista.

En aquí donde se produce la asignación y así SwiftUI ya puede empezar a enterarse de los cambios en nuestro modelo de datos.

Los elementos involucrados

La creación del NSFetchRequest no tiene nada de especial, nos limitamos a devolverlo sin necesidad de usar las funciones fetch o count del NSManagedObjectContext para consumir los resultados, de eso ya se encargará @FetchRequest

Ahora nos toca ir a la vista donde queremos presentar el resultado de este NSFetchRequest, que es FavoritesView y declarar el @FetchRequest que recibe como parámetro el NSFetchRequest que hemos visto antes.

Para presentar los datos en la vista usamos un List(), pero también se podría usar un ForEach, dependiendo de como construyamos la vista.

Y esto es todo, ya no hay que hacer nada más. Fácil ¿verdad?

Con esto terminaría la guía, pero como también queréis saber cómo se añaden los datos al contendor, además de mostrarlos, vamos a seguir un poco más con el tutorial.

Un poco de Combine

Para poder mostrar las sesiones favoritas primero tendremos que poder marcarlas como tales, y esto se hace en la vista de detalle de la sesión.

El botón de la vista nos indica el estado de la sesión, si ya es favorita o por el contrario la podemos añadir a la lista de sesiones que más nos gustan.

Este estado se gestiona mediante el publisher que creamos con el property wrapper @Published que encontramos en la propiedad isFavorite del ViewModel de esta vista. Dicho estado reacciona a los cambios como veremos a continuación.

Como se puede ver la vista declara una variable al ViewMode que contiene la propiedad isFavorite. Dentro del body de la vista apreciamos que en base a su valor el aspecto y contenido del botón varía.

La solucitud de cambio de estado se hace en la función handleFavoriteButtonTap() que dispara el botón. Como se ve, dicha función se limita al cambiar el valor de la propiedad, nada más.

Para poder ver que ocurre cuando se cambia el valor debemos dirigirnos a la clase que hacer de ViewModel, que es SessionViewModel. El código que nos interesa se muestra a continuación.

// 1
Eliminamos la primera entrada que nos llega para procesar, mas que nada porque es la asignación que hacemos justo en la línea superior, y lo que queremos es procesar los cambios en el valor de la variables hechos por el usuario, no por recuperar el estado actual en el que se encuentra la sesión

// 2
Cuando se nos notifica un nuevo valor lo procesamos en el hilo principal

// 3
En función del nuevo valor que nos llega añadimos o eliminamos la sesión de la tabla de favoritos.

¿Y cómo se actualiza el interface de usuario? Aquí es donde entra en juego Combine y su integración con SwiftUI.

En la vista hemos declarado la variable sessionModel como @ObservableObject lo que implica que se notificarán los cambios que en esta se produzcan, y en esta la propiedad isFavorite que al ser declarada como @Published automáticamente publica cualquier cambio de valor que en ella se produzca.

Resmiendo se podría decir que se la serie de hechos sería algo como esto:

  1. El usuario pulsa sobre el botón
  2. La función que controla dicho botón cambia el valor de la variable isFavorite
  3. El ViewModel está suscrito a los cambios de isFavorite, al recibir uno lo procesa.
  4. Como el valor de isFavorite ha cambiado se informa para que la vista vuelva a renderizar el contenido en función de los cambios

Acerca del uso de @Published conviene recordad que sólo puede usarse sobre propiedades contenidas en clases

The @Published attribute is class-constrained. Use it with properties of classes, not with non-class types like structures.

Código Fuente

Como siempre tenéis el código fuente de la app en este repositorio de GitHub.

Publicado en SwiftUI