I've finally at least narrowed down this problem. I'm computing some aggregate functions (as in this example the sum) of expenditures. If I change some expenditures, this aggregate fetch doesn't refresh immediately but only after a while (probably after the changes have been saved to the database). I've found this part in the doc:
- (void)setIncludesPendingChanges:(BOOL)yesNo
As per the documentation
A value of
YES
is not supported in conjunction with the result typeNSDictionaryResultType
, including calculation of aggregate results (such as max and min). For dictionaries, the array returned from the fetch reflects the current state in the persistent store, and does not take into account any pending changes, insertions, or deletions in the context. If you need to take pending changes into account for some simple aggregations like max and min, you can instead use a normal fetch request, sorted on the attribute you want, with a fetch limit of 1.
Ok how can I still include pending changes? I'm using a NSFetchedResultsController
to display my data. And here is my aggregate function:
- (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod
{
NSLog(@"getExpenditures_Start");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Expenditures"];
[fetchRequest setResultType:NSDictionaryResultType];
NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"forSpendingCategory = %@ AND date >= %@", self, startDate];
//Define what we want
NSExpression *keyPathExpression = [NSExpression expressionForKeyPath: @"amount"];
NSExpression *sumExpression = [NSExpression expressionForFunction: @"sum:"
arguments: [NSArray arrayWithObject:keyPathExpression]];
//Defining the result type (name etc.)
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
[expressionDescription setName: @"totalExpenditures"];
[expressionDescription setExpression: sumExpression];
[expressionDescription setExpressionResultType: NSDoubleAttributeType];
// Set the request's properties to fetch just the property represented by the expressions.
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];
NSLog(@"%@", self.managedObjectContext);
// Execute the fetch.
NSError *error = nil;
NSArray *objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (objects == nil) {
return [NSNumber numberWithDouble:0];
} else {
if ([objects count] > 0) {
return [[objects objectAtIndex:0] valueForKey:@"totalExpenditures"];
} else {
return [NSNumber numberWithDouble:0];
}
}
}
EDIT:
*Is a loop through the NSSet
possible and fast enough?*
- (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod
{
NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]];
double total = 0;
for(Expenditures *expenditure in self.hasExpenditures){
if(expenditure.date >= startDate){
total = total + [expenditure.amount doubleValue];
}
}
return [NSNumber numberWithDouble:total];
}
EDIT FINALLY WITH ANSWER Thx to all of you I've finally found the problem in the loop. This works very fast and nice:
- (NSNumber *)getExpendituresAmountForCostPeriod:(CostPeriod)costPeriod
{
NSDate *startDate = [NSDate startDateForCostPeriod:[self getBiggestCostPeriod]];
double total = 0;
for(Expenditures *expenditure in self.hasExpenditures){
if([expenditure.date compare: startDate] == NSOrderedDescending){
total = total + [expenditure.amount doubleValue];
}
}
return [NSNumber numberWithDouble:total];
}
Called from controllerDidChangeContent.
That's enough for today.. :-)