Filtros en Realm con NSPredicate por fecha

Resumen

Filtrar registros en Realm requiere una sintaxis distinta a la de SwiftData, y aquí es donde entra en juego el NSPredicate. Si trabajas con Swift y necesitas consultar una base de datos local con filtros por fecha, dominar los predicados de Realm te permite construir queries potentes, parecidas a SQL, sin salir del ecosistema iOS.

Qué es un NSPredicate y por qué Realm lo necesita

El NSPredicate es el tipo de query que Realm usa para filtrar objetos dentro de su base de datos. A diferencia de SwiftData, Realm no acepta closures de Swift puros: necesita una expresión en formato string que evalúe propiedades de la entidad.

La idea es sencilla. Le pasas un formato tipo "date >= %@ AND date < %@" y dos parámetros que reemplazan esos marcadores. El nombre date debe coincidir exactamente con la propiedad declarada en tu entidad RMRecord, porque Realm hace la evaluación contra el modelo persistido [00:39].

¿Qué es un NSPredicate en Realm? Es una query basada en strings que evalúa propiedades de tus objetos almacenados. Funciona similar a una sentencia WHERE de SQL y soporta operadores como AND, >= o <.

Cómo construir el predicado para el filtro de hoy

La función arranca limpiando el return anterior y dejando dos auxiliares: el Calendar para sus utilidades de fechas y now para representar el instante actual. Luego se declara una variable predicate de tipo NSPredicate y se hace switch sobre el filtro recibido.

Para el caso today, el flujo es así:

  • Calculas el startOfDay y el endOfDay (que en realidad es el inicio del día siguiente).
  • Instancias el predicado con el formato "date >= %@ AND date < %@".
  • Pasas ambas fechas como parámetros, casteadas con as NSDate.

Ese casteo no es opcional. El método startOfDay(for:) devuelve un Date de Swift, pero NSPredicate solo entiende NSDate. Si te saltas el as NSDate, el compilador te lo va a reclamar [01:38].

Por qué replicar el patrón en semana, mes y año

La estructura del predicado es idéntica para los demás casos: cambia únicamente qué fechas calculas como límites. Para la semana usas startOfWeek y endOfWeek, para el mes los equivalentes mensuales, y para el año pasas la fecha de hace 12 meses como primer parámetro y now como segundo [03:00].

Esto te deja un código predecible: una sola plantilla de query, distintos rangos.

Cómo ejecutar la query y devolver el array de records

Una vez que tienes el predicado listo, la consulta a Realm se hace en una línea:

swift let results = realm.objects(RMRecord.self).filter(predicate)

Aquí le indicas dos cosas a Realm: el tipo de objeto que buscas (RMRecord.self) y el predicado por el cual filtrar. El resultado es un Results<RMRecord>, que no es un array nativo de Swift.

Por eso el siguiente paso es convertirlo. Lo transformas en Array, le aplicas un map y llamas toRecord sobre cada elemento. Como RMRecord es conforme al protocolo toRecord, esa función está disponible y devuelve el modelo de dominio que la app espera consumir [04:10].

¿Por qué hay que convertir Results a Array? Porque Results es un tipo propio de Realm con observación en vivo. Para mapear a tus modelos de dominio y desacoplar la capa de datos, lo conviertes a Array antes del map.

Cómo cambiar de SwiftData a Realm en la inyección inicial

Tener la implementación lista no basta. La app sigue inyectando el servicio de SwiftData en su punto de arranque, así que toca abrir la clase principal y reemplazar esa línea.

La práctica recomendada es dejar la línea original comentada y escribir debajo la nueva inyección apuntando a RealmDatabaseService. Así puedes alternar entre ambas implementaciones si necesitas comparar comportamiento o depurar [05:18].

Al correr la app en el simulador, los filtros aparecen vacíos: es una base de datos nueva. Agregando un registro llamado Nuevo registro de Realm con 10 dólares y un gasto de 12, los filtros responden correctamente, lo que confirma que tanto la escritura como la lectura con predicados están funcionando.

Qué viene después: actualizar y eliminar registros

Con el fetch y el save operativos, el siguiente paso natural es completar el CRUD. Las funciones de actualización y eliminación en Realm tienen sus propias particularidades, sobre todo porque Realm exige que las escrituras ocurran dentro de un bloque write.

¿Ya probaste tus predicados con rangos de fechas personalizados? Cuéntame en los comentarios qué filtros estás implementando en tu app.