0

I'm using NSFetchedResultsController to display a table of my NSManagedObject data.

Up to now, I've been using the name property on my objects to sort them:

NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:[Item entityName]];
NSSortDescriptor* nameSorter = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES selector:@selector(caseInsensitiveCompare:)];
request.sortDescriptors = @[nameSorter];
self.frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                   managedObjectContext:self.moc
                                                     sectionNameKeyPath:@"nameInitial"
                                                              cacheName:nil];

Note that my sectionNameKeyPath is different from my sort request. As in this answer, I use a transient property on Item, called nameInitial. Its getter just reads name and returns the first letter.

So far so good. But now, I want to add a special condition: if the first word of the name is 'the', I don't want to sort by that. I want to sort by the first letter of the 2nd word. I can't do this with a transient property because now, the NSSortDescriptor on the fetch request will give a different order than the sectionNameKeyPath, which makes NSFetchedResultsController barf.

So I added a nameInitial field to Item and performed a lightweight migration. Now, I can add a NSSortDescriptor using this new attribute. I just have to populate it with the right letter. This is where my problem comes in: What do I do with the objects I already have in the DB, for which the nameInitial attribute is nil? As far as I can tell, these are my options:

  • Write a code that executes upon the first launch of the new version of the app, reads all the name fields and updates nameInitial appropriately.

  • Use awakeFromFetch to automatically update nameInitial for each object as it is loaded.

  • Override the getter of nameInitial, so it updates itself if it's nil.

I don't particularly like any of these options. The first one isn't elegant at all, and the last two mean either the awakeFromFetch or the getter will have unexpected side-effects. Is there a better way to do this that I'm missing?

Community
  • 1
  • 1
Shinigami
  • 2,123
  • 23
  • 40

1 Answers1

0

You shouldn't do any of those. You should be writing a migration which processes this instead of using a lightweight (auto) migration (which can only fill the values in as nil).

Technically, your suggestions will work, but they aren't 'correct', will run slower and will be a maintenance burden in the future.


From your comment, the best option then is your first suggestion. You don't have many choices - use the built in migration processing or write your own version check and migration logic.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • The problem is I believe Marcus Zarra vehemently advices against using heavy migration to the point where he essentially says "do whatever it takes to avoid it" (paraphrasing). I don't remember why - I've given the book away atm. – Shinigami Apr 16 '14 at 11:55
  • I haven't read that, do you have an online reference? Marcus certainly knows what he's talking about though – Wain Apr 16 '14 at 11:58
  • I don't have the book at the moment, but I think the book followed the gist of [this post](http://stackoverflow.com/a/15800585/1827743). The main objection seems to be its poor performance. However, my data model is very simple. I might actually experiment with it and if it's not unbearable, I'll go with heavy migration. – Shinigami Apr 16 '14 at 12:31