Search code examples
iosswiftcore-datansfetchedresultscontrollernssortdescriptor

Is it possible to use a UUID in an NSSortDescriptor for NSFetchedResultsController?


When using a UUID property of NSManagedObject in an NSFetchedResultsController, the application crashes with:

-[__NSConcreteUUID compare:]: unrecognized selector sent to instance

when the contents of NSFetchedResultsController.fetchedObjects change, but not when it is first loaded. When first loaded, the objects are sorted correctly by UUID.

This is how I create the NSFetchedResultsController:

let fetchRequest: NSFetchRequest<MyObject> = MyObject.fetchRequest()
fetchRequest.sortDescriptors = [
    NSSortDescriptor(key: #keyPath(MyObject.uuid), ascending: true),
]

fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(MyObject.tag), tag)

let controller = NSFetchedResultsController(fetchRequest: fetchRequest,
                                            managedObjectContext: context,
                                            sectionNameKeyPath: nil,
                                            cacheName: nil)
controller.delegate = self
    
do {
    try controller.performFetch()
} catch {
    fatalError("###\(#function): Failed to performFetch: \(error)")
}

To create the NSManagedObject (MyObject in the code above), I used the model editor to add a field "uuid" with the UUID type.

It seems that Core Data has no problem sorting by UUID at the SQL level, but after it loads the data and tries to maintain the sorting in memory, it tries to call compare on _NSConcreteUUID in some way, which doesn't exist as a method. This method could be implemented with byte or string comparison of the UUID.

Failed attempts:

  • Adding compare as an extension to UUID or NSUUID
  • Passing the comparator argument to NSSortDescriptor is not allowed by Core Data and will terminate the application

Maybe there is a workaround by using Transformable that will let it benefit from the native UUID type on the SQL side, but still be usable inside NSFetchedResultsController?


Solution

  • No, it's not possible to sort Core Data results using a UUID property. You would need to convert the UUID to a string if you want to use it for sorting.

    Core Data doesn't actually sort on UUIDs when fetching. You can see this if you turn on Core Data SQLite debugging by adding -com.apple.CoreData.SQLDebug 4 to the arguments in the build scheme in Xcode. When you sort on a numerical property like a time stamp, the debug output includes a SQL ORDER BY clause

    CoreData: sql: SELECT 0, t0.Z_PK FROM ZEVENT t0 ORDER BY t0.ZTIMESTAMP DESC
    

    If you try it with a UUID property, there's no ORDER BY clause. Core Data just ignores the sort descriptor. It won't crash but it's not sorting the results either.

    I'm not sure why this is, because using the sqlite3 command line tool to open up the SQLite file Core Data creates shows that a UUID property is represented as a SQLite BLOB field. SQLite can sort on BLOB, even though it doesn't usually make sense to do so.

    I suggest filing a bug with Apple. I don't know what they'll think about how this should work, but it should definitely not ignore the sort descriptor and not print a console message of some kind.