I’ve just started playing with the iOS development and Core Data technology.
What I am trying to do is to insert object into the Core Data and then run fetch request within same context looking for the newly inserted object.
Here is the code I am using to insert the object:
+(Reward*) rewardForAction: (Actions) action
inManagedObjectContext: (NSManagedObjectContext *) context {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Reward"];
NSPredicate *actionPredicate =
[NSPredicate predicateWithFormat:@"action = %@",
[NSString stringWithFormat:@"%i", action]];
NSPredicate *todayPredicate =
[NSPredicate predicateWithFormat:@"when > %@",
[NSDate today]];
NSLog(@"Today's midnight :%@", [NSDate today]);
request.predicate =
[NSCompoundPredicate andPredicateWithSubpredicates:
[NSArray arrayWithObjects: actionPredicate, todayPredicate, nil]];
NSError *error = nil;
NSArray *matches = [context executeFetchRequest: request error: &error];
Reward *reward = nil;
if(!matches) {
NSLog(@"ERROR: failed to retrieve rewards");
} else if(matches.count > 0) {
NSLog(@"WRONG: reward already exists");
} else {
reward = [NSEntityDescription insertNewObjectForEntityForName:@"Reward"
inManagedObjectContext:context];
reward.action = [NSNumber numberWithInt:action];
reward.when = [NSDate date];
reward.pointsEarned = [Reward getPointsForAction:action];
}
[Stats track: context];
[context save:nil];
return reward;
}
This code first checks if reward is already given today for some action and if not it gives a reward.
calling this method twice with same parameters, like this
[Reward rewardForAction:APPLICATION_LAUNCH
inManagedObjectContext: self.db.managedObjectContext];
[Reward rewardForAction:APPLICATION_LAUNCH
inManagedObjectContext: self.db.managedObjectContext];
expected to result in inserting only one object.
But, in fact it inserts two objects into the Core Data. In the debugger I see second fetch request returns no objects when one is already there.
Looks like NSFetchRequest does not see changes in the data store and operating will on old data. Am I missing something ?
EDIT:
I have also set up observer for the NSManagedObjectContextObjectsDidChangeNotification and when it gets called I am calculating sum of the points to reflect it on the UI. Unfortunately sum also does not contain points from the inserted object. Here is my code to calculate sum:
+(NSInteger) getPoints: (NSManagedObjectContext *) context {
Stats * stats= [Stats get: context];
NSArray *args = [NSArray arrayWithObject:
[NSExpression expressionForKeyPath:@"pointsEarned"]];
NSExpression *ex = [NSExpression expressionForFunction:@"sum:"
arguments:args];
NSExpressionDescription *ed = [[NSExpressionDescription alloc] init];
[ed setName:@"result"];
[ed setExpression:ex];
[ed setExpressionResultType:NSInteger32AttributeType];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setPropertiesToFetch:[NSArray arrayWithObject:ed]];
[request setResultType:NSDictionaryResultType];
/*if([stats intervalStartDate]) {
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@"when >= %@", [stats intervalStartDate]];
[request setPredicate:predicate];
}*/
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Reward"
inManagedObjectContext:context];
[request setEntity:entity];
NSArray *results = [context executeFetchRequest:request error:nil];
if(results && results.count > 0) {
NSDictionary *resultsDictionary = [results objectAtIndex:0];
NSNumber *resultValue = [resultsDictionary objectForKey:@"result"];
return [resultValue intValue];
} else {
return 0;
}
}
Strangely enough when I stop the app on the simulator and run it again. I can see all points as I expect.
From
I assume the “action” attribute is stored as
NSNumber. You should use the same data type in the corresponding predicate:ADDED: To your question about “NSManagedObjectContextDidSaveNotification” vs “NSManagedObjectContextObjectsDidChangeNotification”:
A fetch request with
NSDictionaryResultTypeonly fetches the current state in the persistent store , and does not take into account any pending changes, insertions, or deletions in the context.You can find this information e.g. in the documentation of
setIncludesPendingChanges:.This explains why your fetch request sees only changes that have been saved.