ioscore-datansfetchrequestnssortdescriptor

Sort fetch request based on relationship attribute


Let's say I have two entities; Item and LocalizableString. The LocalizableString entity has two attributes; string and locale. The Item entity has a to-many relationship to LocalizableString called localizedTitles. Where there's one string entity per "locale".

Now, in my app, I have some context/setting that determines what locale I want to use when showing the UI. So let's say I have a UITableViewController with an NSFetchedResultsController handling the data flow.

My question is: can I sort the Item entities based on the LocalizableString's string property where locale is for example en? And can I do this in the fetch request, not after having fetched all items?


Solution

  • Sure you can do that, but you do not have to fetch any particular locale upfront. Your fetched results controller would just fetch the Item objects, and you can then refer to the correct string as you need it.

    One way to implement this is to add a convenience function to the Item class, something like the following

    func titleWithLocale(identifier: String) -> String? {
       if let title = self.localizedTitles.filter 
             { $0.locale == identifier }
             .first as? LocalizableString {
         return title.string
       }
       return nil
    }
    

    Most likely you will have to tweak this code to conform to the strict typing of Swift, but you get the idea.

    If you need to sort by the title string, perhaps you need a different approach. If you know that each Item has a localized title in the respective locale you can fetch the LocalizedTitle entity with a predicate the filters for the locale:

     request.predicate = NSPredicate(format: "locale = %@", "en")
    

    Obviously, you can sort by string with the usual sort descriptors. Now, to populate your table, you get to the item by following the relationshp in the other direction.

     let title = fetchedResultsController.objectAtIndexPath(indexPath)!
     let item = title.item