Adam S
Adam S

Reputation: 16414

CoreData NSFetchRequest with 'count' expression property is returning a NSManagedObjectID instead of an integer count

I have an NSFetchRequest set to fetch properties with enough information to populate a row displaying a user - their company, name, etc, and the number of things assigned to them. I send this to an NSFetchedResultsController which I'm using to populate rows in a table.

The property I'm having trouble with is the count of assigned items. A user can be a part of multiple projects, and I only care about issues within the current project and in a certain set of states.

So I have a predicate set up to get all issues assigned to this user, both in this project and in a given set of states:

NSPredicate* sequenceStatePredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[
        [NSPredicate predicateWithFormat:@"$assignment.workItem.sequence.project == %@", project],
        [NSPredicate predicateWithFormat:@"$assignment.workItem.sequence.state IN %@", [WorkSequence countableStates]]
    ]];

I then set up a count: expression for that predicate, on the assignments relationship of the user:

NSExpression *assignments = [NSExpression expressionForSubquery:[NSExpression expressionForKeyPath:@"assignments"]  usingIteratorVariable:@"assignment" predicate:sequenceStatePredicate];
NSExpression *count = [NSExpression expressionForFunction:@"count:" arguments:@[assignments]];
NSExpressionDescription *sequenceCountProperty = [[NSExpressionDescription alloc] init];
[sequenceCountProperty setExpression:count];
[sequenceCountProperty setExpressionResultType:NSInteger32AttributeType];
[sequenceCountProperty setName:WorkSequenceCount];

Finally, I set this expression as a property for my fetch request:

NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[User entityName]];
[fetchRequest setResultType:NSDictionaryResultType];
[fetchRequest setPropertiesToFetch:@[/* Some other properties */, sequenceCountProperty]];
[fetchRequest setPredicate:query.predicate];
...

This all works fine - except the result I get in the dictionary for the WorkSequenceCount key is an NSManagedObjectID, not an integer.

(lldb) po [[[[_fetchedResultsController fetchedObjects] objectAtIndex:0] objectForKey:WorkSequenceCount] class]
_NSCoreDataTaggedObjectID

The object ID appears to be equivalent to the expected count (note the p50 at the end - I expect the count to be 50 here, and I only have 3 Project objects in CoreData at this time):

(lldb) po [[[_fetchedResultsController fetchedObjects] objectAtIndex:0] objectForKey:WorkSequenceCount]
0xd000000000c80008 <x-coredata://1EB42655-9F98-4DFD-8A7A-BB65A108D300/Project/p50>

And the property expression is correct:

(lldb) po [[[[_fetchedResultsController fetchRequest] propertiesToFetch] objectAtIndex:4] expression]
count:(SUBQUERY(assignments, $assignment, $assignment.workItem.sequence.state IN {1, 2, 3, 5} AND $assignment.workItem.sequence.project == 0x16f93a90 <x-coredata://70C4F650-DADF-462C-A10A-55DA1E5978D3/Project/p1>))

...as is its result type (0x000000c8 == 200 == NSInteger32AttributeType):

(lldb) po [[[[_fetchedResultsController fetchRequest] propertiesToFetch] objectAtIndex:4] expressionResultType]
0x000000c8

If I log the SQL and run the request by hand on the SQLite file, I get the expected results.

If I remove the project filter from the predicate, I get an integer - so this appears to be related to including that. Obviously I can't do that, because then I get the number of items assigned to the user across all projects. No matter how I build the predicate statement (swapping the order of the AND, build it as one predicate with an AND myself instead of using NSCompoundPredicate, etc...) I can't change the result.

So, why is my fetch request returning a managed object ID when I explicitly tell it to give me an integer?

Upvotes: 1

Views: 440

Answers (1)

pbasdf
pbasdf

Reputation: 21536

It's weird - a bug in CoreData, I think. It's certainly not honouring the expressionResultType. I've experienced it with much simpler expressions, and dodged it by using object attributes in expressions/predicates, rather than objects themselves. eg rather than counting assignments, try counting an attribute of the assignments (assuming there is an attribute that you know is not nil).

Upvotes: 1

Related Questions