@Query: leer y filtrar datos
Trae tus datos a la interfaz con @Query, que se actualiza sola cuando cambian. Filtra con #Predicate, ordena, y construye consultas dinámicas.
Guardar datos es la mitad; mostrarlos es la otra. @Query trae tus modelos a la vista y, lo mejor, se mantiene sincronizado: insertas o borras en cualquier parte de la app, y las listas que usan @Query se actualizan solas.
- Leer datos con
@Query. - Ordenar los resultados.
- Filtrar con
#Predicate. - Consultas dinámicas según la interfaz.
@Query: datos reactivos
Declara una propiedad @Query en tu vista y SwiftData la rellena con los objetos del almacén, manteniéndola al día:
struct ListaTareas: View {
@Query private var tareas: [Tarea]
var body: some View {
List(tareas) { tarea in
Text(tarea.titulo)
}
}
}
Fíjate en el círculo completo: una vista inserta una Tarea con el context, y esta lista —que ni sabe de la otra vista— se actualiza automáticamente. @Query observa el almacén; cuando cambia, SwiftUI redibuja. Es la misma reactividad de @State y @Observable, ahora extendida a la base de datos. No escribes código de “recargar la lista”: el dato es la fuente de la verdad, y la interfaz lo sigue. Persistencia y UI, un solo flujo.
Ordenar
Pasa un sort con un key path:
@Query(sort: \Tarea.creada, order: .reverse) private var tareas: [Tarea]
// varios criterios
@Query(sort: [SortDescriptor(\Tarea.completada),
SortDescriptor(\Tarea.titulo)]) private var todas: [Tarea]
Filtrar con #Predicate
#Predicate describe una condición de forma segura y traducible a la base de datos:
@Query(filter: #Predicate<Tarea> { tarea in
!tarea.completada
}) private var pendientes: [Tarea]
Combina condiciones con && y ||, compara textos con .contains, etc. El compilador valida el predicado — nada de strings mágicos.
Consultas dinámicas
Si el filtro depende de algo que cambia (una búsqueda del usuario), no puedes usar @Query estático. Configúralo en el init:
struct Buscador: View {
@Query private var resultados: [Tarea]
init(texto: String) {
_resultados = Query(filter: #Predicate<Tarea> { tarea in
texto.isEmpty || tarea.titulo.contains(texto)
}, sort: \.titulo)
}
var body: some View {
List(resultados) { Text($0.titulo) }
}
}
La vista padre le pasa el texto (por ejemplo, desde un @State con un TextField), y al cambiar, recrea la vista con la consulta nueva.
Combina @Query con .onDelete para el clásico “deslizar para borrar”:
List {
ForEach(tareas) { Text($0.titulo) }
.onDelete { indices in
for i in indices { context.delete(tareas[i]) }
}
}- Muestra tus
Nota(oTarea) con@Queryen unaList. - Ordénalas por fecha de creación descendente.
- Añade un
@Queryfiltrado con#Predicate(solo las no completadas). - Añade “deslizar para borrar” con
.onDeleteycontext.delete. - Inserta desde otra vista y confirma que la lista se actualiza sola.