10

What are the steps that I need to perform to implement user-defined ordering in a UITableViewController with Core Data as the backing store?

Do I still need to respond to -tableView:moveRowAtIndexPath:fromIndexPath:toIndexPath: or is the model re-ordering handled automatically by the table view? Can I just check the "Ordered" checkbox in the Core Data model file and expect it to transmit the changes from the table view to the store, or do I have to do more? Do I have to change the way my fetchedResultsController gets it's objects (e.g. add a sort descriptor)?

nevan king
  • 112,709
  • 45
  • 203
  • 241

3 Answers3

8

You don't need to abandon NSFetchedResultsController or NSOrderedSet. If you stick with NSOrderedSet, the easy way is to just add a method to your objects NSManagedObject subclass. So for instance, if you have an object Book with an ordered set of Chapters, you can add a method to the Chapters object:

- (NSUInteger)indexInBookChapters
{
    NSUInteger index = [self.book.chapters indexOfObject:self];
    return index;
}

Tip: Add this to a category so it doesn't get overwritten by CoreData auto generated code.

Then you can use this method in your sort descriptor for your NSFetchedResultsController:

fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"indexInBookChapters" ascending:YES]];
Richard Venable
  • 8,310
  • 3
  • 49
  • 52
  • Are you saying its a bad solution because it doesn't work for some rare scenarios? If I were to model the rare scenario of ordered many-to-many relationships, I would probably just use a third object, say a BookChapterPair, and I would add the indexing properties there. So I could get the indexes via bookChapterPair.indexInBook or bookChapterPair.indexInChapter. Then just set up the NSFetchedResultsController to fetch the BookChapterPair entity instead of Chapter or Book. Easy! – Richard Venable Nov 01 '12 at 02:41
  • Your solution is not workable for ordered many-to-many relationships, which are hardly "rare" (except in your example of books and chapters). – Ilias Karim Nov 02 '12 at 04:51
  • Introducing a third object may have been the only solution before Core Data supported ordered relationships but is nonetheless a little clunky and doesn't answer the question being asked. – Ilias Karim Nov 02 '12 at 06:08
  • 2
    Actually, in Apple's documentation on Core Data many to many relationships, they recommend a third object for many scenarios, even saying "An advantage of the intermediate entity is that you can also use it to add more information to the relationship—for example a “FriendInfo” entity might include some indication of the strength of the friendship with a “ranking” attribute." In our case the extra information would be the indexes. BTW, I only said it was rare because although I've encountered many uses for many-to-many relationships, I've never run across one that required ordering. Have you? – Richard Venable Nov 02 '12 at 13:01
  • 1
    It make sense that Apple's documentation would mention the only solution that was possible before iOS 5 introduced ordered relationships in Core Data. But I now find adding a third object to the data model simply to maintain order superfluous. Because the question asked about NSOrderedSet, I also assumed the asker wants to learn to use ordered relationships – Ilias Karim Nov 02 '12 at 17:26
  • One example of ordered many-to-many relationships is songs belonging to playlists – Ilias Karim Nov 02 '12 at 17:26
  • 1
    You should read the documentation yourself: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdRelationships.html#//apple_ref/doc/uid/TP40001857-SW10 It doesn't have anything to do with ordered many-to-many, just many-to-many in general. Ie. Even when using NSSet, you often still want a 3rd intermediate object. – Richard Venable Nov 02 '12 at 21:05
  • You're right: using a third object has nothing to do with NSOrderSet, thus it does not belong here. (Thanks for the link) – Ilias Karim Nov 03 '12 at 00:57
  • @RichardVenable, Thanks for your tip about adding a category to a model! – dmirkitanov Mar 19 '13 at 20:29
  • 4
    @RichardVenable Did you tried that? ```NSFetchedResultsController``` will not work with such sort descriptor, if you use SQLite store. – Shmidt Jan 14 '14 at 13:33
  • @Shmidt, yes I use this method but not with an SQLite store. I thought someone else had put a comment previously pointing out that it doesn't work with SQLite, but I don't see that comment anymore. – Richard Venable Jan 15 '14 at 17:12
1

The best practice here is to NOT use fetchedResultsController because it requires a sort descriptor, as you mentioned. You could use a dummy property but this is clearly not good practice. Instead, use objectAtIndex in tableView:cellForRowAtIndexPath: and reload the table data when you need to, perhaps using Key-Value Observing, though depending on your use case there may be simpler and less crash-prone approaches.

And yes, you will have to implement tableView:moveRowAtIndexPath:fromIndexPath:toIndexPath:

See also

NSFetchedResultsController and NSOrderedSet relationships

Community
  • 1
  • 1
Ilias Karim
  • 4,798
  • 3
  • 38
  • 60
0

I'd an an attribute to the Core Data entity, let's say it's an NSNumber called sortOrder.

When you init a new object in this list, assign the sortOrder in increasing order, so that the default sort order is there.

Last, tell your fetchedResultsController to use an extra sortDescriptor for this sortOrder attribute.

When a user adjusts the position of an entry, you could set the sortOrder to the mean of the entry before and after it.

(Just an idea --- I haven't dealt with this problem or had to implement it, but was considering it for my project down the line.)

bryanjclark
  • 6,247
  • 2
  • 35
  • 68
  • Thanks for the answer, but this is what I want to avoid doing. Supposedly, the new APIs in Core Data in iOS 5 will do ordering for you automatically. It's a bit of a hairy problem to implement, especially with re-ordering. – nevan king Mar 26 '12 at 16:10
  • Nice! I'll have to check in on this question once it gets a good answer then :) – bryanjclark Mar 26 '12 at 16:55
  • 1
    Also: apparently iCloud does not support ordered data; if you're syncing with iCloud then you are out of luck for that: "When used with iCloud, there are several limitations with the SQLite store at this time: Ordered relationships are not supported." https://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html – bryanjclark Mar 26 '12 at 20:33
  • Yeah, I just saw that today too. I can't understand at all why ordering isn't compatible with iCloud, but I'd say it will be in the future. The limitation on data migration is probably the same. – nevan king Mar 26 '12 at 21:52
  • 2
    It's actually incredibly lame that core data doesn't support mapping an ordering + displaying onto tableviews and collectionviews out of the box! – fatuhoku Apr 06 '14 at 16:02