views:

189

answers:

4
    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];
+2  A: 

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).

Peter Hosey
I defined 'bestTime' in the xcdatamodel file in my project just like all the other things that I am accessing. I have tried copy and pasting the name directly from that file.
danpalmer
Is it definitely in the same entity you are trying to access?
Joshua
Yes. I have put in some logs before the line that fails to print out other attributes that I know work. I have compared the output to what it should be and it is the same model.
danpalmer
+1  A: 

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.

Nick Farina
I will have a look at that. Thanks. As for the initialisation, I don't know. I executed a fetch request on my managed object context, and put the results in to a mutable dictionary. The object is just taken from that based on which tableviewcell is pressed.
danpalmer
danpalmer: Please edit your question to include the fetch request.
Peter Hosey
+1  A: 

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).

piobyz
Puzzle is not a class I have written inheriting from NSManagedObject, it is just an NSManagedObject. From what I have read about Core Data, this is an ok way to do it if you don't have any code that needs to run on the object. For me, it is simply a collection of properties. That is all.
danpalmer
+1  A: 

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.

danpalmer
I've been seeing this issue a lot recently and most of the time it appears to be caused by the app bundle in the simulator retaining an old .mom file from a previous version of the data model. For some reason, it is not always purged when the model changes. Rebooting the machine caused the simulator to build an entirely new app directory thus resolving the problem. Deleting the app from the simulator will fix it as well.
TechZen
I had tried deleting the app in the simulator. The issue was actually with Xcode thinking it had put the 'bestTime' attribute in, but not actually having done it.
danpalmer