Core Data error: 'Objects must be sorted by section name' for specific languages such as Thai
Asked Answered
O

2

8

CoreData: error: (NSFetchedResultsController) The fetched object at index 72 has an out of order section name 'อั. Objects must be sorted by section name'

I am using the following code to sort by book title field and display book title's first upper case letter as the section name in a UITableViewController.

The code runs perfectly in all languages except of Thai. I read on the Internet that there are special non US characters causing such issues (i.e. Æ), but I didn't find any solution yet.

See gschandler response on The fetched object at index [i] has an out of order section name 'å

Code of FRC is

 NSFetchedResultsController *aFetchedResultsController = 
    [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                managedObjectContext:managedObjectContext
                                  sectionNameKeyPath:@"titleFirstLetter"
                                           cacheName:nil];

Code of firstLetter is:

- (NSString *)titleFirstLetter {

    // support UTF-16:
    NSString* val = [self.title substringWithRange:[self.title rangeOfComposedCharacterSequenceAtIndex:0]];

    return [val uppercaseString];
}

Any suggestions?

Odont answered 29/4, 2014 at 10:34 Comment(4)
I have exactly the same problem, every language except thai works. Did you find any workaround? Is it gschandler's approach that is best?Spirited
I used gschandler's approach at first as I thought that Thai is the only problematic language... However, when I found that some characters in German are also causing this issue I decided that solving this issue is a must. My solution was simply adding a 'titleFirstLetter' field in the sqlite database instead of getting this field at runtime. This way all the sort work is done by the sqlite database and no conflict ever arise. Surely this is not the cleanest solution in terms of DB design, but this workaround works perfectly.Odont
Ah...now I remember, I had a simular problem in another app with CoreData and creating values in runtime, which ended up with extra-field-work-around instead. Seems to be the safest way. Thank you! :-)Spirited
In my case I was sorting alphabetically but one field had an invisible newline char which resulted in the wrong sectionIndex being calculated for it. Hopefully this helps someone.Harleigh
H
6

You have to add a sort descriptor that sorts on the same property the sectionNameKeyPath does.

NSSortDescriptor *sort = [[NSSortDescriptor alloc]initWithKey:@"titleFirstLetter" ascending:NO];   
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sort,nil]];
[fetchRequest setFetchBatchSize:20];
Hemelytron answered 5/6, 2014 at 15:16 Comment(0)
L
2

Another example for SwiftUI 3:

    @SectionedFetchRequest(sectionIdentifier: \.categoryName,
                           sortDescriptors: [NSSortDescriptor(keyPath: \Listing.category, ascending: false)],
                           predicate: NSPredicate(format: "%K = %d", #keyPath(Listing.isPublished), true),
                           animation: .default)

.categoryName is derived from Listing.category as an extension of listing, like this:

extension Listing {
    @objc var categoryName: String {
        return self.category?.name ?? ""
    }
}

However, I was receiving an error because I was attempting to sort listings by createdOn: [NSSortDescriptor(keyPath: \Listing.createdOn, ascending: false)]

As @BossBols stated, the sort descriptors must be the same for the section and the main items. So sorting listings on category works. Adding a second sectioned sort enables my original intended behavior.

    @SectionedFetchRequest(sectionIdentifier: \.categoryName,
                           sortDescriptors: [NSSortDescriptor(keyPath: \Listing.category, ascending: false),
                                             NSSortDescriptor(keyPath: \Listing.createdOn, ascending: false)],
                           predicate: NSPredicate(format: "%K = %d", #keyPath(Listing.isPublished), true),
                           animation: .default)
    var listings: SectionedFetchResults<String, Listing>
Lunitidal answered 6/8, 2021 at 22:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.