Estoy buscando entidades de Core Data en SwiftUI y me gustaría ordenarlas así:

  1. Si hay una próxima fecha, ordénelas ascendentes
  2. Debajo de los que tienen una próxima fecha, ordene el resto por fecha de creación

Tengo:

@FetchRequest(entity: MyEntity.entity(), sortDescriptors: [
    NSSortDescriptor(keyPath: \MyEntity.nextDate, ascending: true),
    NSSortDescriptor(keyPath: \MyEntity.dateCreated, ascending: true)
]) private var entities: FetchedResults<MyEntity>

El problema es que aquellos que no tienen una próxima fecha se ordenan por encima de aquellos que no tienen una próxima fecha, como se ve en este otro pregunta.

Traté de subclasificar NSSortDescriptor para mover los valores nil hasta el final, pero nunca llama a ninguna de las funciones anuladas.

final class NilsLastSortDescriptor: NSSortDescriptor {
    override func copy(with zone: NSZone? = nil) -> Any {
        return NilsLastSortDescriptor(key: self.key, ascending: self.ascending, selector: self.selector)
    }

    override var reversedSortDescriptor: Any {
        return NilsLastSortDescriptor(key: self.key, ascending: !self.ascending, selector: self.selector)
    }

    override func compare(_ object1: Any, to object2: Any) -> ComparisonResult {
        if (object1 as AnyObject).value(forKey: self.key!) == nil && (object2 as AnyObject).value(forKey: self.key!) == nil {
            return .orderedSame
        }
        if (object1 as AnyObject).value(forKey: self.key!) == nil {
            return .orderedDescending
        }
        if (object2 as AnyObject).value(forKey: self.key!) == nil {
            return .orderedAscending
        }
        return super.compare(object1, to: object2)
    }
}

También intenté usar la API NSSortDescriptor que incluye un bloque de comparación, sin embargo, esto falla

Excepción: "NSSortDescriptor no compatible (no se admiten bloques de comparación)"

¿Cómo podría lograr el comportamiento de clasificación deseado?

1
Jordan H 23 feb. 2020 a las 22:17

2 respuestas

La mejor respuesta

@FetchRequest usa el descriptor de ordenación como parte de una solicitud de búsqueda, y en consecuencia no puede usar bloques de comparación en la definición de la solicitud de búsqueda (al menos, no con un almacén SQLite). Sin embargo, puede ordenar los resultados de la búsqueda (entities) como parte del procesamiento de body de la vista, utilizando sorted (by :):

var body: some View {
        VStack {
        List{
            ForEach(self.entities.sorted(by: { first, second in
                if first.nextDate == nil && second.nextDate == nil { return (first.dateCreated <= second.dateCreated) }
                if first.nextDate == nil { return false }
                if second.nextDate == nil { return true }
                if first.nextDate! == second.nextDate! { return (first.dateCreated <= second.dateCreated) }
                return (first.nextDate! < second.nextDate!)
            }), id: \.self) { myEntity in
                // Your code for your cells...
                ...
            }
        }
    }
}

Supuse por simplicidad que dateCreated no es opcional. De lo contrario, deberá modificar el predicado para que la ordenación maneje los valores nulos. Probablemente también podría escribirse con más precisión, pero espero que entiendas la idea.

Solo para tener en cuenta que se está ordenando en la memoria, por lo que no tengo idea del rendimiento y / o impacto de la memoria de esta solución.

0
pbasdf 24 feb. 2020 a las 18:21

En CoreData, la ordenación solo usa un método de la clase NSString como "compare:" o "localizedStandardCompare:" algo así.

Como no puede anular esos métodos o incluso cambiarlos en la mayoría de los casos, puede considerar otras soluciones.

En lugar de la fecha "Cero", puede establecer un Special Date que es muy tarde si los datos deseados son 'Cero'. De esta manera, puede ordenar esas fechas en la parte inferior. No puede mostrar esas fechas especiales, o mostrarlas como "Cero" en SwiftView.

Puedes jugar al juego NSString, no al subclass

0
E.Coms 24 feb. 2020 a las 16:49