views:

776

answers:

3

Hi everyone,

first time I post to this round, so please bear with me if I don't follow all the rules properly.

I am writing an app for the iPhone (OS 3.1) and am trying to write some code which lets me add decimals. I have a Core Data entity called SimpleItem with a amount attribute. Here is the test case I wrote:

// Create and configure objects
    SimpleItem *si1 = [self createSimpleItem:@"si1"];
    si1.amount = [NSDecimalNumber decimalNumberWithMantissa:1000 exponent:0 isNegative:NO];
    SimpleItem *si2 = [self createSimpleItem:@"s12"];
    si2.amount = [NSDecimalNumber decimalNumberWithMantissa:2000 exponent:0 isNegative:NO];
    // Need to save prior to fetching results with NSDictionaryResultType (known limitation)
    [self save]; 

    // Describe fetch request
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"SimpleItem" inManagedObjectContext:self.context];
    [request setEntity:entityDescription];
    [request setResultType:NSDictionaryResultType];

    NSExpression *keyPathExpression = [NSExpression expressionForKeyPath:@"amount"];
//  For whatever reason, evaluating this expression here is absolutely not working. Probably decimals aren't handled properly.
    NSExpression *sumAmountExpression = [NSExpression 
              expressionForFunction:@"max:"
              arguments:[NSArray arrayWithObject:keyPathExpression]];

    NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
    [expressionDescription setName:@"amount"];
    [expressionDescription setExpression:sumAmountExpression];
    [expressionDescription setExpressionResultType:NSDecimalAttributeType];
    [request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];

    // Fetch the amounts
    NSError *error = nil;
    NSArray *array = [self.context executeFetchRequest:request error:&error];

If I execute this code through otest and debug it, I get an exception when the fetch request is executed: "-[NSDecimalNumber count]: unrecognized selector sent to instance."

Just evaluating the keyPathExpression without the aggregate function works fine, though.

The reference documentation shows exactly the same example so I'm wondering what I am doing wrong. Or could this be just a bug?

All the best, Harald

A: 

I think I had this problem before. IIRC, the problem is that you've set your fetch result type to NSDictionaryResultType but you're receiving it in an NSArray. Try switching the fetch result type to NSManagedObjectResultType which does return an array. All the examples in the docs use the default NSManagedObjectResultType.

In any case, the error message clearly results from an NSDecimal object being sent an array's count message. You could confirm this by trapping the result in an id. eg:

id *genericObj = [self.context executeFetchRequest:request error:&error];  
NSLog("returned object=%@",genericObj);
NSLog("returned object class=%@",[genericObj class]);
TechZen
When specifying a `NSDictionaryResultType` you will receive an array containing one object via `executeFetchRequest:error:`.
Ashley Clark
That's what I get for winging it from memory.
TechZen
A: 

Hi,

thanks for letting me know. The thing is that I actually don't want to use managed objects. After all, I am interested in the amounts only and this is what I get with the dictionary.

Also, trying to investigate the issue as you described didn't work as executing the fetch request immediately fails and I don't get anything back.

Any other ideas? Is there any other possibility to get aggregate values? Any "best practices"? At the moment I do the following (continuation of the code above):

NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init]; [expressionDescription setName:@"amount"]; [expressionDescription setExpression:keyPathExpression]; [expressionDescription setExpressionResultType:NSDecimalAttributeType]; [request setPropertiesToFetch:[NSArray arrayWithObject:expressionDescription]];

// Fetch the amounts
NSError *error = nil;
NSArray *array = [self.context executeFetchRequest:request error:&error];
if (array == nil) {
 STFail(@"Fetch request could not be completed: %@", [error localizedDescription]);
}
STAssertEquals([array count], (NSUInteger) 2, @"Number of fetched objects != 2");

// Manually calculate the sum
NSDecimalNumber *sum = nil;
for (NSDictionary * dictionary in array) {
 NSDecimalNumber *amount = (NSDecimalNumber *) [dictionary valueForKey:@"amount"];
 if (!sum) {
  sum = amount;
 } else {
  sum = [sum decimalNumberByAdding:amount];
 }
}
STAssertNotNil(sum, @"Sum is nil");
STAssertTrue([sum isEqualToNumber:[NSDecimalNumber decimalNumberWithMantissa:3000 exponent:0 isNegative:NO]], 
      @"Sum != 3000: %@", sum);

Regards, Harald

Harald Schubert
Hey Harald, please don't post your responses as an answer to your question. Instead, you should use the "add comment" feature to directly reply to answers/comments and edit your question with additional information as you find it.
Travis Bradshaw
You need a certain amount of reputation to comment, updating the original question is fine though... just add a new section with the updated info so you can still see the original question
monowerker
A: 

Copying your given source to a new iPhone project and running that in the simulator worked fine here. I was compiling against the 3.1.2 SDK on Snow Leopard.

This is what I used for the data model:

model description

There must be something else going on causing your issue. Can you describe your model or simplify it further?

Ashley Clark
See my comment to the original question; I were you using an SQL store?
Jesse Rusak