views:

301

answers:

4

How could I use the NSFetchedResultsController with translated sort key and sectionKeyPath?

Problem: I have ID in the property "type" in the database like typeA, typeB, typeC,... and not the value directly because it should be localized. In English typeA=Bird, typeB=Cat, typeC=Dog in German it would be Vogel, Katze, Hund.

With a NSFetchedResultController with sort key and sectionKeyPath on "type" I receive the order and sections - typeA - typeB - typeC

Next I translate for display and everything is fine in English: - Bird - Cat - Dog

Now I switch to German and receive a wrong sort order - Vogel - Katze - Hund

because it still sorts by typeA, typeB, typeC

So I'm looking for a way to localize the sort for the NSFetchedResultsController.

I tried the transient property approach, but this doesn't work for the sort key because the sort key need to be in the entity.

I have no other idea. But I can't believe that's not possible to use NSFetchedResultsController on a derived attribute required for localization?

There are related discussions like http://stackoverflow.com/questions/1384345/using-custom-sections-with-nsfetchedresultscontroller but the difference is that the custom section names and the sort key have probably the same order. Not in my case and this is the main difference.

At the end I would need a sort order for the necessary NSSortDescriptor on a derived attribute, I guess. This sort order has also to serve for the sectionKeyPath.

Thanks for any hint.

A: 

At the end I would need a sort order for the necessary NSSortDescriptor on a derived attribute, I guess.

For the sorting, one possibility would be to do something like:

[NSSortDescriptor initWithKey:@"type" 
 ascending:YES 
 selector:@selector(translatedCompare:)]

where translatedCompare is a comparison method you write (as a category on NSString) that localizes the values before comparing them.

Not sure about how to handle the sectionKeyPath.

David Gelhar
Yes, I tried this to a certain point. But to be honest, I was not able to convince NSString using this category defined compare method.... I didn't expect sectionKeyPath to be a problem because I could use the transient property here which should match the sort order of the localized sort from the sort descriptor.
Gerd
I have given it another try. Please refer to the answer "I tried again with the custom compare..." (Too long for a comment).
Gerd
A: 

I guess that the problem is caused by the cache.

You may set the cache with the given name when you create NSFetchedResultsController object by using the following method. The last variable is the cache name.

- (id)initWithFetchRequest:(NSFetchRequest *)fetchRequest managedObjectContext:(NSManagedObjectContext *)context sectionNameKeyPath:(NSString *)sectionNameKeyPath cacheName:(NSString *)name

The NSFetchedResultsController uses the cache to calculate the sections and the ordering if the cache with the same name exists. And the cache is written on the disk (not memory).

So if you change the language between English and German, you should delete the cache. To delete the cache you can use the class method deleteCacheWithName: .

You can find the detail information here. http://developer.apple.com/iphone/library/documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40008227-CH1-SW24

natsu
Thanks for your answer. I tried this, but didn't see a difference. The problem occurs not only while switching from one language to another, it happens also if I stay in one language setting.
Gerd
A: 

I tried again with the custom compare. The compare method itself works if I call it manually in my controller. But as soon as I supply this compare method to the sort descriptor used to build the fetch request for NSFetchedResultController the app crashes in performFetch:

#0  0x97ffb4e6 in objc_exception_throw
#1  0x00376786 in -[NSSQLGenerator generateSQLStatementForFetchRequest:ignoreInheritance:countOnly:]
#2  0x00376022 in -[NSSQLAdapter _newSelectStatementWithFetchRequest:ignoreInheritance:]
#3  0x00375d61 in -[NSSQLAdapter newSelectStatementWithFetchRequest:]
#4  0x00375c2e in -[NSSQLCore newRowsForFetchPlan:]
#5  0x003754a0 in -[NSSQLCore objectsForFetchRequest:inContext:]
#6  0x00375113 in -[NSSQLCore executeRequest:withContext:]
#7  0x0037495a in -[NSPersistentStoreCoordinator(_NSInternalMethods) executeRequest:withContext:]
#8  0x0037152b in -[NSManagedObjectContext executeFetchRequest:error:]
#9  0x00425b7c in -[NSFetchedResultsController performFetch:]

I doublechecked my code with the provided compare method localizedCompare: and it works seamlessly. But not with my own one, even if I simply return NSOrderedSame in my compare method the app crashes. A breakpoint in my own compare method never fires. So for me it looks like that any self supplied compare method is simply not possible. Anybody success with self defined compare method for the NSSortDescriptor for a fetch request?

Gerd
I ran into this exact problem too. The issue is that the the fetched results controller is having the database do the sort for you since it is more efficient at doing things like that. You can't specific a custom selector if you are using core data with a sqlite backend. Unfortunately the only thing you can really do is sort it in memory.
Mike
Thanks Mike. Good to know that I'm not the only one... At the end I ended up sorting in memory (see above). It's working fine since I (personally ;-)) control the max. number of rows. But just imagine that would not be the case.
Gerd
A: 

OK, not a nice solution but at the end it's working (because I have a defined limited set of records ca. 100):

On intializing the app:

  • I create an "order by" attribute in the managed object.
    • I check if the localization (and with it the sort order) changed since last time. If so:
    • I fetch all the records and sort it in an NSArray by localized names.
    • I write back the records in the store

For perfomance reason I only fetch and sort records according to a NSPredicate.

Than I can use all the existing code using the "order by" as sort key and section key path.

I know that I could use my ordered array as datasource for the table view, but I wanted to keep the existing code and use the methods of NSFetchedResultsController.

As a convenience of this I have full control over the sorting, that will fit my needs in future since I plan to build a more complex sort ordering (location based, higher probability of use of the records at the top, etc.)

However it's not an elegant solution.

Gerd