I'm having the same problem as this question, but the answer of #davedelong is not working for me.
When following the Apple Example, for fetching the smallest date in a set of object I get the following error
-[NSDate count]: unrecognized selector sent to instance
My understanding is that NSExpression's max: only support NSArrays. So I need an other solution.
#davedelong suggested using an ascending NSSortDescriptor, and so I did :
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.entity = [NSEntityDescription entityForName:NSStringFromClass([GCSession class])
inManagedObjectContext:self.objectContext];
fetchRequest.fetchLimit = 1;
NSSortDescriptor* sort = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:YES];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
GCSession* session = [[self.objectContext executeFetchRequest:fetchRequest error:nil] lastObject];
return session.startDate;
The problem here is that the session object returned from the fetch doesn't seems to be the one with the smallest startDate. In my tests, it seems that it even returned the newest date but it doesn't seems consistent.
I could also fetch every GCSession object and sort them but that seems way overkill, especially that GCSession will be augmenting in number when the users will use the application.
Edit : A test project that demonstrate the bug in Apple's example code.
A sort only comes into play after you've fetch the objects. You've set a fetch limit of one and no predicates which tells the fetch "go grab any random single GCSession object". One the fetch has an array 1 element long, it then sorts it, which is useless.
If you want to use a sort to find a min or max, you have to fetch all the objects and then sort them. Removing the line:
fetchRequest.fetchLimit = 1;
… should allow the code to work.
However, you should be able to fetch min and max values with expressions. It's kind of a basic operation.
Edit: Look at the comments for more detail, but Apple's example can work if you change the backing store to SQL instead of XML
Related
In raw SQL it would be simple to sort with all bobs coming first.
How can I do this using a NSFetchedResultsController and NSSortDescriptor?
I think that you cannot do that with NSSortDescriptor, because this is for key-value sorting and does not support expressions.
However, it could work with the help of NSFetchRequest. Set the result type of the fetch request to dictionary
fetchRequest.resultType = NSDictionaryResultType;
Then you can set the properties to fetch to the properties you want plus add a expression for computed properties:
NSExpression *expression = [NSExpression expressionWithFormat:#"name=='bob'"];
NSExpressionDescription *expressionDescription = [NSExpressionDescription new];
expressionDescription.name = "isCalledBob";
expressionDescription.expression = expression;
expressionDescription.resultType = NSBooleanAttributeType;
[fetchRequest propertiesToFetch:#[…, expressionDescription];
Then you can use NSSortDescriptor on the key isCalledBob.
With this you get dictionaries instead of managed objects.
Typed in Safari, did not test it, my kid wakes up in some minutes.
The solution I found to this is kind of problem is to just add an extra field to help with the FRC. ie isBob which gets updated whenever the name is set.
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?
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.
I have a fairly large set of Core Data entities with a date property. I'm creating a report from the entities and need to find the report date range. There are ~1000 records within the report.
I'm thinking of getting the fetched objects array from my NSFetchedResultsController and sorting the array using date sort descriptor. Then getting the first and last object of that array to determine the date range. I seem to recall that date operations are expensive and am not sure if sorting an array that way, getting a 1000 element array back is a good idea.
Is there some other core data or predicate trick that I can use to query my core data stack to find an object with the minimum or maximum date?
Here's the code that I'm currently using, but am wandering if there's something faster and more efficient:
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSArray* sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray* sortedDateArray = [NSMutableArray arrayWithArray:[fetchedResultsController.fetchedObjects sortedArrayUsingDescriptors:sortDescriptors]] ;
NSManagedObject* firstDateObject = [sortedDateArray objectAtIndex:0];
NSManagedObject* lastDateObject = [sortedDateArray lastObject];
No need to use a FetchedResultsController. Just use a simple FetchRequest, and take advantage of the aggregate functionality.
The following document actually has an example of fetching based on the minimum date attribute of an entity.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html#//apple_ref/doc/uid/TP40002484-SW6
if (win) {
// Game was won, set completed in puzzle and time
// Calculate seconds taken
int timeTaken = (int)([NSDate timeIntervalSinceReferenceDate] - self.gameStartTime);
int bestTime = [[self.puzzle valueForKey:#"bestTime"] intValue];
if (timeTaken < bestTime && bestTime != 0) {
[self.puzzle setValue:[NSNumber numberWithInt:timeTaken] forKey:#"bestTime"];
NSLog(#"Best time for %# is %#", [self.puzzle valueForKey:#"name"], [self.puzzle valueForKey:#"bestTime"]);
}
}
This is some code from an iPad game I am making and I am using Core Data for storing the levels. When a level is completed and won, I want to set the best time for that level. The time taken is calculated, and if it is better than the previous best time, I want to set it as the best time for the level.
This code fails on the 'int bestTime' line when it tries to retrieve the best time from self.puzzle which is an NSManagedObject from Core Data. The best time is stored as an Integer 32 in the Core Data model. It fails with a SIGABRT error.
'[<NSManagedObject 0x95334d0> valueForUndefinedKey:]: the entity Puzzle is not key value coding-compliant for the key "bestTime".'
I have searched online for reasons as to why this is happening and how to fix it, but nothing seems to have helped. There are other places where I access Integer values from the Core Data model and they work perfectly, although they are used to filter and sort queries.
I also don't know if the line where I set the value will work.
Any help on this would be greatly appreciated.
EDIT: This is the code that fetches an array of puzzles of which one is taken to be the above puzzle.
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Puzzle" inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Set the filter for just the difficulty we want
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"difficulty == %d", difficulty];
[request setPredicate:predicate];
// Define how we will sort the records
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"sortid" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
// Fetch the records and handle an error
NSError *error;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
Ok, Firstly, I would like to thank everyone who suggested ideas. They may not have helped me solve the problem, but I learnt more about Core Data and it is always good to find out what I should be checking when things don't work.
I don't really know what the problem was. Until this morning I had Xcode open for about 5 days I think and yesterday I added the attribute 'bestTime' to the data model. I can only assume that over the 5 days, Xcode had become a little unstable and thought it was saved when it wasn't. I had checked that I had saved the model attributes, in fact I must have checked 3 or 4 times as well as my habit of hitting Command+S after any change I make.
Anyway, I rebooted my machine earlier today and when I started up Xcode a few minutes ago I realised that 'bestTime' was not in the model file. I added it, reset the settings on the iPad simulator and it worked.
Thank you all again for the help, sorry the solution wasn't more interesting and code based. Although it makes me feel better that my code wasn't the cause.
That managed object doesn't have an attribute named “bestTime”. According to the exception message, it definitely is a Puzzle, so you haven't declared an attribute named bestTime in your model (or you misspelled it or capitalized it differently).
I did solve the same problem by delete and create the data model again and clean then rebuild again.
I think the bug is caused by core data does not update some data inside sometimes.
I don't think there's enough information here to determine the cause. You might try reading the Core Data Troubleshooting Guide; one possible cause could be if you initialized this particular instance of Puzzle using plain init rather than initWithEntity.
If you added attribute bestTime to the model at the later time, you might have forgotten to put declaration and implementation for them in the connected Managed Object Class.
Try convenience actions provided in Design -> Data Model -> Copy Objective-C ... Method Declarations/Implementations to Clipboard (when editing your Model file).
If parsing JSON into a managed object, be sure you're using the coreDataPropertyName property rather than the json-key-name key from JSON. Easy to mix up when they're named so similarly.
This error was driving me nuts, and all because I was using image-url rather than imageURL.
trying the solution here:
https://stackoverflow.com/questions/1741093?tab=newest#tab-top
I'm using a transient property and the category solution and it seems to be working right up until the index char starts to wrap around to the A's again, not sure why it's doing that, just logging what the category/transient getter is returning for uppercaseFirstLetterOfName.
I'm sorting using the name property and then setting sectionNameKeyPath on the fetchRequest to uppercaseFirstLetterOfName.
The full error is:
NSFetchedResultsController ERROR: The fetched object at index 248 has an out of order section name 'Y. Objects must be sorted by section name'
Any ideas where I might have gone wrong or how to even track down the problem?
I found the problem.
Because the sort was producing caps and lower case dups I suppose but the indextitles weren't I got that out of order index/section name:
just added this to the fetch
selector:#selector(caseInsensitiveCompare:)
so it is now:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)];
and works
cracker jack!