How to implement a transient property on core data? - ios

I have this core data entity called Countries. This entity has a field called nameOfCountry that contains country names in english. I need to localize this to other languages, so I have created a transient property called nameOfCountryLocalized.
On the Countries class I am importing this category
Countries+NameOfCountryLocalized.h
#import "Countries.h"
#interface Countries (NameOfCountryLocalized)
#property (nonatomic, strong) NSString * nameOfCountryLocalized;
#end
Countries+NameOfCountryLocalized.m
#import "Countries+NameOfCountryLocalized.h"
#import "Countries.h"
#implementation Countries (NameOfCountryLocalized)
#dynamic nameOfCountryLocalized;
-(NSString *) nameOfCountryLocalized {
[self willAccessValueForKey:#"nameOfCountryLocalized"];
NSString *nameLocalized = NSLocalizedString(self.nomePais, nil);
[self didAccessValueForKey:#"nameOfCountryLocalized"];
return nomeLocalizado;
}
-(void)setNameOfCountryLocalized:(NSString *) nameLocalized {
[self willChangeValueForKey:#"nameOfCountryLocalized"];
[self setNomePaisLocalizado:];
[self didChangeValueForKey:#"nameOfCountryLocalized"];
}
#end
when I try to access nameOfCountryLocalized using this from a tableViewController
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Countries" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"nameOfCountryLocalized" ascending:YES];
[fetchRequest setSortDescriptors:#[sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:#"Root"];
_fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
I see this error:keypath nameOfCountryLocalized not found in entity
any clues?

The NSFetchedResultsController cannot sort by a transient property. The FRC applies the sort to the underlying SQL store. With a transient property the FRC can group results of a fetch into sections. This group is created by assigned the transient property to sectionNameKeyPath.
Pulling all entities into an array will become a pain once your data grows. The FRC does provide nice support for larger data sets and you don't really want to loose that.
You might be ok assuming that your users don't switch language very often. If that is the case I suggest you write the localized country name into the store as a "normal" property and the FRC can do the sort on that property.
Although when a user does switch language then you would need to update all Country entities.

Your Countries class is not an entity. It is a class for an individual object in the database.
You really should rename it to Country, as it's incorrect to refer to NSManagedObject subclasses as plural. There's a reason Apple didn't name the class NSManagedObjects.
Because your property is added to individual objects but not the entities themselves, it is only available after objects have been fetched from the database. It cannot be used as part of fetching the objects.
You're going to need to fetch the results first (all of them) into an NSArray and then sort the objects by applying the NSSortDescriptor to the NSArray. You might want to do this by creating a wrapper class around NSFetchedResultsController.
Alternatively, put a nameOfCountryLocalized property in the actual database, with english values, and then if the user doesn't use english write to the database changing everything to the correct values. This would allow you to use NSFetchedResultsController exactly as you're trying to do now. I recommend this approach if your database is huge... but it's not, there are only a couple hundred countries in the world so performance is a total non-issue.

Related

How to give a sorting predicate in Core data model editor [duplicate]

The Core Data Documentation states that:
The fetch request associated with the [fetched] property can have a sort ordering, and thus the fetched property may be ordered.
How do I specify the sort descriptors for the fetched property in Xcode's data model editor? I can't find a relevant field anywhere. I'm developing for the iPhone platform, if this makes any difference.
If this is not possible via the graphical model editor, how do I go about modifying the fetch request for the fetched property in code so that it has a sort descriptor?
You can actually grab the model fetched property and add the sort descriptors to it (again, in code). I did this in the standard method that XCode generates in your AppDelegate if you choose one of the templates with Core Data:
By the way. This sorts ALL fetched properties on ALL models in your data model. You could get fancy and adaptive with it, but it was the most succinct way to handle sorting the 7 separate models that each had fetched properties that needed to be sorted by name. Works well.
/**
Returns the managed object model for the application.
If the model doesn't already exist, it is created by merging all of the models found in the application bundle.
*/
- (NSManagedObjectModel *)managedObjectModel {
if (managedObjectModel != nil) {
return managedObjectModel;
}
managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:nil] retain];
// Find the fetched properties, and make them sorted...
for (NSEntityDescription *entity in [managedObjectModel entities]) {
for (NSPropertyDescription *property in [entity properties]) {
if ([property isKindOfClass:[NSFetchedPropertyDescription class]]) {
NSFetchedPropertyDescription *fetchedProperty = (NSFetchedPropertyDescription *)property;
NSFetchRequest *fetchRequest = [fetchedProperty fetchRequest];
// Only sort by name if the destination entity actually has a "name" field
if ([[[[fetchRequest entity] propertiesByName] allKeys] containsObject:#"name"]) {
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortByName]];
[sortByName release];
}
}
}
}
return managedObjectModel;
}
You don't specify them in the graphical editor (as far as I know).
You specify them in the code where you make the fetch.
NSFetchRequest* request = [[NSFetchRequest alloc] init];
NSEntityDescription* entity = [NSEntityDescription entityForName:#"whatYouAreLookingFor"
inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
// here's where you specify the sort
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:#"name" ascending:YES];
NSArray* sortDescriptors = [[[NSArray alloc] initWithObjects: sortDescriptor, nil] autorelease];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:#"myCache"];
The modeling tool doesn't appear to have a way to set the sort descriptors on the fetch request.
It should be possible[1] to, after loading the model but before associating it with a persistent store coordinator, to find the fetched property descriptions for which you want to control the sort order, and replace their fetch requests with fetch requests that have sort descriptors set on them.
[1] In principle this should work. In practice, I have not done so or tested it.
Using Tim Shadel's great answer I added per-NSManagedObject subclass sorting...
...in Tier.m (which is a NSManagedObject subclass)...
+ (void)initialize
{
if(self == [Tier class])
{
NSFetchedPropertyDescription *displayLessonPropertyDescription = [[[Tier entityDescription] propertiesByName] objectForKey:#"displayLesson"];
NSFetchRequest *fetchRequest = [displayLessonPropertyDescription fetchRequest];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:#"displayOrder" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortByName]];
[sortByName release];
}
}
For a single fetched property, Swift 4, Xcode 9.4:
// retrieve the fetched property's fetch request
let fetchedPropertyRequest = (modelName.entitiesByName["entityName"]!.propertiesByName["fetchedPropertyName"] as! NSFetchedPropertyDescription).fetchRequest
// set up the sort descriptors
let sortDescriptors = [NSSortDescriptor(key: "keyName", ascending: true)]
// add the sort descriptors to the fetch request
fetchedPropertyRequest!.sortDescriptors = sortDescriptors
Here's the same thing the loooonnnnnnggggggg way:
// retrieve the fetched property's fetch request
let theEntityDescription: NSEntityDescription = modelName.entitiesByName["entityName"]!
let theFetchedPropertyDescription = theEntityDescription.propertiesByName["fetchedPropertyName"]! as! NSFetchedPropertyDescription
let theFetchedPropertyRequest = theFetchedPropertyDescription.fetchRequest
// set up the sort descriptors
let sortDescriptor1 = NSSortDescriptor(key: "keyName", ascending: true)
let theSortDescriptors = [sortDescriptor1]
// add the sort descriptors to the fetch request
theFetchedPropertyRequest!.sortDescriptors = theSortDescriptors
Note: for this example, I force-unwrapped values. Make sure that you account for optional values in your actual code!
Sadly, though, the ability to sort is somewhat limited. For example, you cannot take a field that is an NSString containing a number, and sort it numerically, at least not with a SQLite backing store. As long as you are sorting alphabetically on strings, numerically only on values stored as numbers and so forth, though, the NSSortDescriptor applied to the fetch request works just fine.
Put this into your NSManagedObject subclass:
+ (void)initialize
{
if (self != [EntityManagedObjectSubClass class]) return;
NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
NSEntityDescription *entityDescription = [managedObjectModel entitiesByName][#"entityName"];
NSFetchedPropertyDescription *fetchedPropertyDescription = [entityDescription propertiesByName][#"fetchedPropertyName"];
NSFetchRequest *fetchRequest = [fetchedPropertyDescription fetchRequest];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"sortDescriptorKey" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
}
Replace EntityManagedObjectSubClass, entityName, fetchedPropertyName and sortDescriptorKey with your own stuff.
Jeff, if the strings are right-aligned, you could just sort on the strings; " 123" > " 23" and so on. But iirc ascii space is after the numbers, and if so, then what you would do is create a dynamic property that is an NSNumber (which supports the compare: method), and use the numberFromString: method to make a number from the string. Then you can specify the number field in the sort. In the interface:
#property NSString *stringIsaNumber; // in the data model
#property NSNumber *number;
in the implementation:
#dynamic stringIsaNumber;
- (NSNumber *) number ;
{ return [self.stringIsaNumber numberFromString]; }
- (void) setNumber:(NSNumber *)value;
{ self.stringIsaNumber = [NSString stringWithFormat:#"%5i",value) }
ps plz forgive coding errors, this is off the top of my head.

Optimizing UITableView backed by NSFetcchedResultsController

I recently switched my CoreData backed UITableViews to use a NSFetchedResultsController instead of an NSArray. For one of the tables, scrolling is now very slow, and I think I know why, but I don't know yet what would be the best solution to fix this.
There are two entities Book and Author which are in a many-to-many relationship. Each cell displays the book title, plus the author. If there is more than one author, it will just display the main author. Each Author has an "order" attribute, which is set when the data is imported.
What I have been doing so far is every time the author name is accessed, my Book class returns a mainAuthor property (an NSString):
- (NSString *) mainAuthor
{
if (!mainAuthor)
{
NSSortDescriptor *sortOrder= [NSSortDescriptor sortDescriptorWithKey: #"order" ascending: YES];
NSArray *authorsSorted = [self.authors sortedArrayUsingDescriptors: #[sortOrder]];
Author *a = authorsSorted[0];
mainAuthor = [NSString stringWithFormat: #"%#", a.name];
}
return mainAuthor;
}
For whatever reason this is now called many times instead of only once and causing the slow down. Maybe NSFetchedResultsController fetches the references over and over when scrolling the table?
So how can I fix this? One possibility is to make mainAuthor an attribute instead of a property. So it is set immediately when the data is imported. But before I start messing with my dataModel I'd like to know if this would be the way to move forward, or maybe there is an alternative solution?
UPDATE 1: Here is the code where I set up the fetchController:
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSManagedObjectContext *moc = [NSManagedObjectContext MR_defaultContext];
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName: #"Book"];
// sort the books by publishing date
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey: #"date" ascending: YES];
[fetchRequest setSortDescriptors: #[sort]];
// only get the books that belong to the library of the current viewController
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"libraries contains[cd] %#", self.library];
[fetchRequest setPredicate: predicate];
[fetchRequest setRelationshipKeyPathsForPrefetching: #[#"authors"]];
[fetchRequest setFetchBatchSize: 10];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest: fetchRequest
managedObjectContext: moc
sectionNameKeyPath: nil
cacheName: nil];
frc.delegate = self;
_fetchedResultsController = frc;
return _fetchedResultsController;
}
Based on your sample code I'd assume that mainAuthor is not in your Core Data schema. As Core Data handles the lifetime (faulting) of your managed objects you should add this property to your Book entity to avoid unpredictable results by using a transient attribute.
Despite this I'd recommend to return an Author object instead of a NSString as you might change name of the attribute in the future or want to use additional information of the mainAuthor in your UI.
Transient Attribute
Add a transient attribute mainAuthor to your Book's entity and add a custom accessor to your Book's class:
- (Author *)mainAuthor
{
[self willAccessValueForKey:#"mainAuthor"];
Author *value = [self primitiveValueForKey:#"mainAuthor"];
[self didAccessValueForKey:#"mainAuthor"];
if (value == nil)
{
NSPredicate *filterByMinOrder = [NSPredicate predicateWithFormat:#"order == %#.#min.order", self.authors];
value = [[self.authors filteredSetUsingPredicate:filterByMinOrder] anyObject];
[self setPrimitiveValue:value forKey:#"mainAuthor"];
}
return value;
}
The disadvantage of using a transient is that you have to make sure that the data is always up-to-date during the lifetime of the appropriate book. So you have to reset mainAuthor in:
willTurnIntoFault
awakeFromFetch
awakeFromSnapshotEvents:
(Optional, but necessary if the user can change the data)
addAuthorObject:
removeAuthorObject:
by calling [self setPrimitiveValue:nil forKey:#"mainAuthor"].
Hint: Better and faster is to create a synthesized primitiveMainAuthor instead of using primitiveValue:forKey:: Managed Object Accessor Methods
Update
Have you tried to set a fetchBatchSize in your NSFetchedResultsController's fetchRequest? Docs: NSFetchRequest fetchBatchSize
Update 2
Yes, setting the appropriate relationship in setRelationshipKeyPathsForPrefetching is necessary in that case.
To identify bottlenecks it's also really helpful to set the debug argument -com.apple.CoreData.SQLDebug 1 to see the SQL statements created by Core Data. This also often helps to understand the different NSFetchRequest attributes and their impacts.

ios core data to AZ indexed tableview

I'm trying to show the values of my core data model to an A-Z indexed table based on the first letter on my attributes (similar to the iOS address book app). The "Favorites" entity of my core data model has 2 attributes: username and status. I want to display only the usernames with status = accepted to the A-Z indexed table.
Here is my code:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Favorites" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSString *status = #"accepted";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"status == %#",status];
[fetchRequest setPredicate:predicate];
// Create the sort descriptors array.
NSSortDescriptor *usernameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"username" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:usernameDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"username" cacheName:#"Root"];
self.fetchedResultsController = aFetchedResultsController;
fetchedResultsController.delegate = self;
return fetchedResultsController;
}
Now when I'm trying to access the section name I get (null)
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSLog(#"%#",[[[fetchedResultsController sections] objectAtIndex:section] name]);
return [[[fetchedResultsController sections] objectAtIndex:section] name];
}
Also I thing that with that way I will get the the name and not the first char in order to display it as a section title.
You need to access the sectionsInfo object properly:
id <NSFetchedResultsSectionInfo> info =
[fetchedResultsController sections][section];
return [info name];
However, this will give you a heading for each unique name, probably not want you want. Instead, you have to give your entity a transient property e.g. NSString *sectionIdentifier and write a getter for it that returns the first letter of the username attribute.
If want an index from A-Z running down on the right edge of the table view you additionally have to implement:
sectionIndexTitlesForTableView: and
tableView:sectionForSectionIndexTitle:atIndex:.
If you still get null for your titles, maybe they are not set or persisted in your entity? Maybe you got zero results? Maybe your fetchedResultsController is nil? There are a number of flaws in your data model, so this seems quite possible.
Your entity name Favorites is plural. That is not logical, you should name it Favorite as one instance only describes one favourite.
The status is a string which is also very inefficient. Instead, you should use a number and apply some enum scheme.
The username is a property of Favorite. That seems also very messy because presumably, you also have a User entity which has a username attribute. You should use a relationship to model this.
use NSFetchedResultsController's sectionIndexTitles function to get array of first char

Adding attribute value to core data IF value already exists

I have to say that everyone on the forum has been really helpful with my attempts at learning core data.
I am adding attribute values to my core data entities and creating a relationship when the user selects a row as shown below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:#"Added to Routine!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
NSManagedObjectContext *context = [self managedObjectContext];
// Create a new device
ExcerciseInfo *info = [_fetchedResultsController objectAtIndexPath:indexPath];
NSManagedObject *routineEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Routines"inManagedObjectContext:context];
NSManagedObject *routineEntityDetail = [NSEntityDescription insertNewObjectForEntityForName:#"RoutinesDetails" inManagedObjectContext:context];
[routineEntityDetail setValue:routineEntity forKey:#"routineinfo"];
[routineEntity setValue: RoutineText forKey:#"routinename"];
[routineEntityDetail setValue: info.details.muscle forKey:#"image"];
How would I include an IF statement whereby if the routinename already exists the new entry would be added to the existing relationship?
Is this easily possible from the current code? So the Test Routines will be grouped instead of showing separately and the detail view would include both entries. Would NSPredicatebe appropriate here? Or perhaps the use of distinctUnionOfObjects?
You thoughts and comments will be appreciated.
AS REQUESTED -
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Routines" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"routinename" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName: nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
You don't need to care about this, you can simply insert a new RoutineDetails object and Core Data takes care of the rest. When you auto-generate the NSManagedObjectsubclasses of your DB tables, Core Data automatically generates accessor methods for all relationships, where you can add and remove single or multiple instances of RoutineDetails from Routines.
To create NSManagedObject subclasses, go to your ManagedObjectModel click on Editor->Create NSManagedObject subclass… and create subclasses for your entities:
In your new subclass, you'll see methods like:
#interface Routines (CoreDataGeneratedAccessors)
- (void)addRoutineDetailsObject:(RoutineDetails *)value;
- (void)removeRoutineDetailsObject:(RoutineDetails *)value;
- (void)addRoutineDetails:(NSSet *)values;
- (void)removeRoutineDetails:(NSSet *)values;
Than you can access all the RoutineDetails directly through the Routines object (you'll get an NSSet of RoutineDetails), and furthermore you can add new objects with
[routineEntity addRoutineDetailsObject:routineEntityDetail];
not caring if an object already exists or not. Core Data will do the right thing.
Comment regarding Core Data: Core Data can have a lot boilerplate code for simple things like fetching. Take a look at the MagicalRecord project, which is much less verbose and does wonders.
EDIT: Whenever you previously created NSManagedObject, now you can directly use your own classes, i.e. Routines. That way you'll have access to all the "new functionality".

Core Data sort tableview by formatted date

I know how to sort Core Data objects in a tableview by NsDate, but this by default seems to create a new section for each object. I want to sort them by a medium formatted date with NSDateFormatter. How would I do this?
For example, if I have 3 objects created on the same day, I want them to be in the same section with the section title being that Day, no time needed.
Each object has an NSDate property. Thanks for your help.
This is the code I have in fetchedResultsController with rgeorge's suggestions. What am I missing here?
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
NSLog(#"get old fetched controller");
return fetchedResultsController;
}
else{
NSLog(#"get new fetched controller");
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"InTextEntity" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *dateDescriptor = [[NSSortDescriptor alloc] initWithKey:#"dateModified" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:dateDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"mediumFormattedDate" cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![fetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
return fetchedResultsController;
}
(I'll write this up assuming you're using an NSFetchedResultsController to drive your tableview. If you're not, I recommend checking it out.)
An interesting feature of NSFetchedResultsController's sectioning abilities: although the property you sort on must be a modeled property (because sqlite does the actual sorting), the property you group the sections with need not be. The only requirement is that the grouping be consistent with the ordering. (i.e., sorting by the sort property will put the objects with matching group properties next to each other.)
So just add something like this to your modeled object class:
// in interface
#property (nonatomic, readonly) NSString *mediumFormattedDate;
// in impl
-(NSString *)mediumFormattedDate
{
// this can be fancier if you need a custom format or particular timezone:
return [NSDateFormatter localizedStringFromDate:self.date
dateStyle:NSDateFormatterMediumStyle
timeStyle:NSDateFormatterNoStyle];
}
(no need to mention mediumFormattedDate in the .xcdatamodel at all.)
Then go ahead and sort your objects by the date property, but group them by your new property. When you create your NSFetchedResultsController, do so along these lines:
NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:#"MyFancyEntity"];
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:#"date"
ascending:YES];
[fr setSortDescriptors:[NSArray arrayWithObject:sd]];
NSFetchedResultsController *frc =
[[NSFetchedResultsController alloc] initWithFetchRequest:fr
managedObjectContext:myManagedObjectContext
sectionNameKeyPath:#"mediumFormattedDate"
cacheName:nil];
// then do stuff with frc
That's all it takes! I've done this in a few apps to get date grouping and it works well.
Sounds like you're setting the section index on the fetched results controller to be your date property, which seems undesirable.
Instead you should probably be computing the section index yourself, and sorting by date. You can accomplish this in either your data model or by computing the sections manually in code.
For example, you could add a property to your managed object model called "Day" and set that to whatever value you want to use (you don't specify if its something like Monday or an actual date like 21).
You can then pass that property to the fetched results controller.
Alternatively you could implement the sections yourself, days are easy, its Monday-Sunday. Dates are a bit harder, 1-28,30,31 depending on what month it is. Then use an appropriate NSPredicate / NSFetchRequest to get the count of the items in each section.

Resources