I am using an NSFetchedResultsController to load messages for different email accounts into a tableview and a collection view. Everything works great on iOS7, and everything works great everywhere in iOS6 EXCEPT when I use a combined OR predicate.
Now I know that an NSFetchedResultsController will return the same item twice if it matches multiple of these predicates. My problem is that the message entity ONLY MATCHES ONE OF THESE PREDICATES yet is being returned the same number of times as predicates.
So for some reason the NSFetchedResultsController is saying these messages pass all the predicates, but only on iOS6. It is doing this for every single message from a particular email account, but not for messages from the other accounts.
If I adjust the OR predicate to go off of ObjectIDs instead, unique results are returned. However my UI will no longer update properly as values are adjusted
For the life of me I can't seem to figure out how on earth these entities are passing the predicates for other email accounts and folders.
My predicate construction that results in duplicates is as follows. Account -> messages is a oneToMany relationship, folders <-> messages is a manyToMany.
NSMutableArray *predicates = [NSMutableArray array];
for (CRMFolder *folder in comboFolder.folders) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND folders contains %@ AND account = %@", folder, folder.account];
[predicates addObject:predicate];
}
fetchRequest.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
So if I change that to objectIDs like so, I get unique results:
NSMutableArray *predicates = [NSMutableArray array];
for (CRMFolder *folder in comboFolder.folders) {
NSArray *objectIDs = [folder.messages valueForKey:@"objectID"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND self in %@", objectIDs];
[predicates addObject:predicate];
}
fetchRequest.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
But then if I combine these two, I STILL GET DUPLICATES.
NSMutableArray *predicates = [NSMutableArray array];
for (CRMFolder *folder in comboFolder.folders) {
NSArray *objectIDs = [folder.messages valueForKey:@"objectID"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isMarkedForDelete != YES AND isTopReferenceMessage = YES AND folders contains %@ AND account = %@ AND self in %@", folder, folder.account, objectIDs];
[predicates addObject:predicate];
}
fetchRequest.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
Any ideas On
1) Why are these entities passing predicates they shouldn't?
2) How come this only occurs on iOS6?
3) Whats the fix?
I know I can use returnsDistinctResults, but that requires converting everything to dictionaries, which currently isn't an option.
EDIT
I just did a test, and if I exclude the account creating the issues, results are normal. Similarly I just excluded all other accounts, and no duplicates. So it is only when these predicates all combine this occurs. I'm really suspect of some bug in the fetched results controller.
EDIT 2
Here is the raw CoreData Log for a fetch that produces several duplicates. Note there are 3 messages that it duplicates 9 times to get 27 results.
CoreData: annotation: sql connection fetch time: 0.0528s
CoreData: sql: SELECT t0.Z_PK, t1.Z_14BLINDCARBONCOPYMESSAGES FROM Z_3BLINDCARBONCOPYMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3BLINDCARBONCOPIES WHERE t1.Z_14BLINDCARBONCOPYMESSAGES IN ( 276, 276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14BLINDCARBONCOPYMESSAGES ASC
CoreData: annotation: sql execution time: 0.0004s
CoreData: annotation: Prefetching from join table for many-to-many relationship "blindCarbonCopies" from database. Got 0 rows
CoreData: sql: SELECT t0.Z_PK, t1.REFLEXIVE FROM Z_14REFERENCEMESSAGES t1 JOIN ZCRMMESSAGE t0 ON t0.Z_PK = t1.Z_14REFERENCEMESSAGES WHERE t1.REFLEXIVE IN ( 276, 276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.REFLEXIVE ASC
CoreData: annotation: sql execution time: 0.0525s
CoreData: annotation: Prefetching from join table for many-to-many relationship "referenceMessages" from database. Got 0 rows
CoreData: sql: SELECT t0.Z_PK, t1.Z_14CARBONCOPYMESSAGES FROM Z_3CARBONCOPYMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3CARBONCOPIES WHERE t1.Z_14CARBONCOPYMESSAGES IN ( 276, 276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14CARBONCOPYMESSAGES ASC
CoreData: annotation: sql execution time: 0.0270s
CoreData: annotation: Prefetching from join table for many-to-many relationship "carbonCopies" from database. Got 0 rows
CoreData: sql: SELECT t0.Z_PK, t1.Z_14SENDERMESSAGES FROM Z_3SENDERMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3SENDERS WHERE t1.Z_14SENDERMESSAGES IN ( 276, 276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14SENDERMESSAGES ASC
CoreData: annotation: sql execution time: 0.0007s
CoreData: annotation: Prefetching from join table for many-to-many relationship "senders" from database. Got 6 rows
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZLABEL, t0.ZNAME, t0.ZPRIMARYADDRESS, t0.ZPROFILEPHOTO, t0.ZPROFILEPHOTOLASTCACHEDATE, t0.ZPROFILEPHOTOSOURCE, t0.ZCONTACT, t0.ZUSER FROM ZCRMADDRESS t0 WHERE t0.Z_PK IN (?,?,?)
CoreData: annotation: sql connection fetch time: 0.0004s
CoreData: annotation: total fetch execution time: 0.0007s for 3 rows.
CoreData: annotation: Prefetching with key 'senders'. Got 3 rows.
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZCONTENTID, t0.ZCONTENTTYPE, t0.ZDATA, t0.ZDATE, t0.ZEXCHANGEID, t0.ZFILEEXTENSION, t0.ZISLOCAL, t0.ZISTEMPORARY, t0.ZORIGINALFILENAME, t0.ZSIZEINBYTES, t0.ZTHUMBNAIL, t0.ZMESSAGE, t0.ZUSER FROM ZCRMATTACHMENT t0 WHERE t0.ZMESSAGE IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY t0.ZMESSAGE
CoreData: annotation: sql connection fetch time: 0.0005s
CoreData: annotation: total fetch execution time: 0.0007s for 0 rows.
CoreData: annotation: Prefetching with key 'attachments'. Got 0 rows.
CoreData: sql: SELECT t0.Z_PK, t1.Z_14REPLYTOMESSAGES FROM Z_3REPLYTOMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3REPLYTOS WHERE t1.Z_14REPLYTOMESSAGES IN ( 276, 276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14REPLYTOMESSAGES ASC
CoreData: annotation: sql execution time: 0.0006s
CoreData: annotation: Prefetching from join table for many-to-many relationship "replyTos" from database. Got 6 rows
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZLABEL, t0.ZNAME, t0.ZPRIMARYADDRESS, t0.ZPROFILEPHOTO, t0.ZPROFILEPHOTOLASTCACHEDATE, t0.ZPROFILEPHOTOSOURCE, t0.ZCONTACT, t0.ZUSER FROM ZCRMADDRESS t0 WHERE t0.Z_PK IN (?,?,?)
CoreData: annotation: sql connection fetch time: 0.0004s
CoreData: annotation: total fetch execution time: 0.0006s for 3 rows.
CoreData: annotation: Prefetching with key 'replyTos'. Got 3 rows.
CoreData: sql: SELECT t0.Z_PK, t1.Z_14RECIPIENTMESSAGES FROM Z_3RECIPIENTMESSAGES t1 JOIN ZCRMADDRESS t0 ON t0.Z_PK = t1.Z_3RECIPIENTS WHERE t1.Z_14RECIPIENTMESSAGES IN ( 276, 276, 276, 276, 276, 276, 276, 276, 276, 286, 286, 286, 286, 286, 286, 286, 286, 286, 624, 624, 624, 624, 624, 624, 624, 624, 624 ) ORDER BY t1.Z_14RECIPIENTMESSAGES ASC
CoreData: annotation: sql execution time: 0.0006s
CoreData: annotation: Prefetching from join table for many-to-many relationship "recipients" from database. Got 6 rows
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZEMAIL, t0.ZLABEL, t0.ZNAME, t0.ZPRIMARYADDRESS, t0.ZPROFILEPHOTO, t0.ZPROFILEPHOTOLASTCACHEDATE, t0.ZPROFILEPHOTOSOURCE, t0.ZCONTACT, t0.ZUSER FROM ZCRMADDRESS t0 WHERE t0.Z_PK IN (?)
CoreData: annotation: sql connection fetch time: 0.0004s
CoreData: annotation: total fetch execution time: 0.0007s for 1 rows.
CoreData: annotation: Prefetching with key 'recipients'. Got 1 rows.
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZENCRYPTEDBODY, t0.ZENCRYPTEDHTMLBODY, t0.ZENCRYPTEDPLAINTEXTBODY, t0.ZENCRYPTEDSUBJECT, t0.ZMESSAGE FROM ZCRMMESSAGEBODY t0 WHERE t0.ZMESSAGE IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
CoreData: annotation: sql connection fetch time: 0.0011s
CoreData: annotation: total fetch execution time: 0.0015s for 3 rows.
CoreData: annotation: Prefetching with key 'messageBody'. Got 3 rows.
CoreData: annotation: total fetch execution time: 0.1482s for 27 rows.
folders contains ...
is a wrong predicate usage. contains is only valid for string value matching. – Dan Shelly