NSSet in NSPredicate, only works if first i issue a nsset.count - iphone

I have a workaround for my problem, but I really want to understand why I'm having this problem and how to solve it.
I have an entity A related to another entity B, and some of the rows of A have a special mark in one field.
I want to count how many of those A that are related to B, have this special mark.
All works perfectly, if after I create the NSSet I count the set:
NSSet *productsSet = currentList.ingredients;
int countProducts = productsSet.count;
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"self IN %# AND isInList = %d", productsSet,1];
[fetchRequest setPredicate:predicate];
int totalProductsInList = [context countForFetchRequest:fetchRequest error:error];
If I comment the
int countProducts = productsSet.count;
I have those errors:
-[NSNull unsignedIntValue]: unrecognized selector sent to instance 0x22b9cd8
2011-12-05 12:10:41.418 xxxxxxxxxx[32964:1bb03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[NSNull unsignedIntValue]: unrecognized selector sent to instance 0x22b9cd8'
isInList is a Int16
thanks,
EDIT:
If I move the 1 inside the NSPredicate, without a count, I get the same error:
[NSPredicate predicateWithFormat: #"self IN %# AND isInList = 1", productsSet;
EDIT 2:
As I can't find why it doesn't work, I check for productsSet.count and if it's greater than 0 I make the NSFetchrequest, problem solved.

Well, after creating the NSSet I check for nsset.count, and in case it's greater than zero I issue the NSFetchrequest, in case it's zero, don't.
Problem solved, but I still have the curiosity for this strange error that I have if I don't use the nsset.count.
thanks,

NSSet object returned by relationship is not an ordinary NSSet but an object of _NSFaultingMutableSet class. And it seems it's not safe to use it in NSPredicate. May be it turns into fault state or something. The reason of the problem looks the same as in Core Data/NSOperation: crash while enumerating through and deleting objects.
So the best way is to use result of allObjects or create a new NSSet object:
NSArray *productsArray = [currentList.ingredients allObjects];
or
NSSet *productsSet = [NSSet setWithSet:currentList.ingredients];

Related

NSPredicate does not get executed on first launch [duplicate]

I am using UIManagedDocument with Parent Child context.
In my child context I do the following
Code 1
NSSet *results = [self.event.memberships filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return ([[evaluatedObject deleted] boolValue] == NO);
}]];
Above code returns the expected results (only Not deleted members for the event).
Code 2
But this code does not. It fetches all records.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"deleted == NO"];
NSSet *results = [self.event.memberships filteredSetUsingPredicate:predicate];
It seems confusing. Both should return same results, but predicateWithBlock returns correct results where as predicateWithFormat returns all records.
What are the pros and cons of using predicateWithBlock instead of predicateWithFormat?
The problem is that you have defined an attribute deleted for your entity. That conflicts with the isDeleted method of NSManagedObject, so you should rename that attribute.
The following "experiment" shows that strange things happen if you call your attribute "deleted" (c is a managed object with a custom deleted attribute):
// Set custom "deleted" property to YES:
c.deleted = #YES;
// Use the property, as your Code 1
NSLog(#"%#", [c deleted]);
// Output: 1
// Use Key-Value Coding, as your Code 2
NSLog(#"%#", [c valueForKey:#"deleted"]);
// Output: 0
// Now really delete the object and try again:
[context deleteObject:c];
NSLog(#"%#", [c valueForKey:#"deleted"]);
// Output: 1
Your "Code 1" refers to the property, therefore it returns the expected result. "Code 2" uses Key-Value Coding, and [c valueForKey:#"deleted"] returns YES if the object
actually has been deleted from the context!
So renaming that attribute should solve your problem. Unfortunately the compiler does not
emit warnings if an attribute name conflicts with a built-in method.
Use the formatting placeholder to replace the bool value:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#",
#"deleted", #(NO)];
Your use of the key path is probably ok, but the right-hand side probably doesn't look like "NO" to the parser.

MagicalRecord sorting using by KVC

I used KVC until now to access object's properties.
In my object i have a method like this:
-(Address *)mainAddress {
if (self.addresses != nil) {
return [self.addresses anyObject]; //stub method
}
else {
return nil;
}
}
I can use this method with KVC using
mystring = [cliente valueForKeyPath:#"mainAddress.city"];
but i cannot use to create a NSFetchRequestController (this code use MagicalRecord)
NSFetchedResultsController *acontroller = [Customer fetchAllSortedBy:#"mainAddress.city" ascending:ascending withPredicate:companyPredicate groupBy:nil delegate:self];
This is the error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath mainAddress.city not found in entity <NSSQLEntity Customer id=4>'
In order to use the sorting with an NSFetchedResultsController, your mainAddress keyPath needs to be an attribute on your entity. NSFRC will sort the data not using KVC in memory, but using the underlying data store. Bottom line answer: make mainAddress a field on your entity in the data model.

CoreData predicate for foreign key with NSObject

I have a relation in Core Data like this:
A -> B
and the following tables:
A
----
id
path
ref_foreignkey_B
B
----
id
name
The problem is that the foreign key is set as B object (inherits from NSObject).
Then I am trying to fetch all entries from A where a reference to a certain B object is set. I know the object B but cannot figure out how to set the NSPredicate correctly.
I have tried the following:
NSString *complexPredicateFormat = [NSString stringWithFormat:#"ZREF_B == %#", self.B];
NSPredicate *filter = [NSPredicate predicateWithFormat:complexPredicateFormat];
[fetchRequest setPredicate:filter];
... but that results in the following error:
'NSInvalidArgumentException', reason: 'Unable to parse the format string "ZREF_B == <B: 0x89d8300> (entity: B; id: 0x89d3ca0 <x-coredata://...
Does anyone know how to set a NSManagedObject (updated) as predict filter (represents the where clause)?
EDIT:
I am sorry. Of course A and also B inherits from NSManagedObject and NOT NSObject.
Based on Gabriel suggestion:
Have you created a Data Model? Why your entities subclass NSObject?
Then
If ref_foreignkey_B is a relationship for entity B you need to use
[NSPredicate predicateWithFormat:#"ref_foreignkey_B == %#", self.B];
Obviously if you use that predicate you need to query (use a fetch request) against A and not B.
NSFetchRequest* fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"A"];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"ref_foreignkey_B == %#", self.B];
Some Notes
Why do you use ZREF_B? This is the column created in the sqlite file (I suppose). You don't have to use that but only properties (attributes or relationships) declared in the data model.
id, path and ref_foreignkey_B for A
id and name for B
When you deal with Core Data you deal with an object graph and not with the db (sqlite for example) itself. This is an important point to understand. You have not to deal with the sqlite file (for example) that is created for you. Only with the data model you have created.
In addition, I suggest you to create an inverse relationship from B to A. This lets you to maintain integrity in the object graph. For info read Relationships.
Hope it helps.
Not sure if I understand the question, but if "ref_foreignkey_B" is the relationship from A to B then your predicate should look like this:
NSFetchRequest *fetch = [NSFetchRequest alloc] init];
fetch.entity = [NSEntityDescription entityForName:#"A" inManagedObjectContext:managedObjectContext];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"ref_foreignkey_B == %#", self.B];
NSError *error = nil
NSArray *array = [managedObjectContext executeRequest:fetch];
...

Is it still true that it's a bad idea to name attributes and relationships like any non-parameter method of NSObject or NSManagedObject?

I did some little experimentation on this.
I created an boolean attribute, named isFault. You know, that's a method of NSManagedObject and therefore actually not allowed for an attribute name because of KVC.
Simply, I used the default Core Data template for this test, but created the data model programmatically so I can show you what I do.
So here we go:
NSAttributeDescription *badAttr = [[NSAttributeDescription alloc] init];
[badAttr setName:#"isFault"];
[badAttr setAttributeType:NSBooleanAttributeType];
[badAttr setOptional:YES];
// don't want to occupy you with the whole, non-important rest ...
Next, I've modified the -insertNewObject method of the controller, added these lines:
// Assume: An managed object is created into the context, but not saved yet...
NSLog(#"isFault = %d", [newManagedObject isFault]); // 0 = NO
BOOL isFault = [[newManagedObject valueForKey:#"isFault"] boolValue];
NSLog(#"isFault = %d", isFault); // 0 = NO
[newManagedObject setValue:[NSNumber numberWithBool:YES] forKey:#"isFault"];
isFault = [[newManagedObject valueForKey:#"isFault"] boolValue];
NSLog(#"isFault = %d", isFault); // 0 = NO
Like you can see, I'm not able to set the isFault attribute to YES. It remains NO. Now, changing the attribute name to isFaultXYZ, will allow that.
So actually, what I wanted to ask is... since this stuff seems to depend on KVC, does the rule only apply to methods that return something and have no parameter? And does it matter what data type is returned? For example, -changedValues has no parameter and returns an NSDictionary. But since there is no attribute type like that, would this cause a collision anyways?
So, here is the evidence. When naming the attribute changedValues, THIS happens after attempting to access it via KVC:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFDictionary boolValue]: unrecognized selector sent to instance 0x1205680'
2010-06-10 14:52:40.232 CoreData[33996:207] Stack: (
8035,
4437,
8825,

Core Data, NSPredicate and to-many key

I have a Core Data model in which a Task entity includes an optional to-many relationship excludedOccurrences. One of the properties of excludedOccurrences is start, which is an NSDate object. The ExcludedOccurrence entity has an inverse mandatory to-one relationship to the Task entity.
In order to fetch tasks for a specified day, I need to make sure that the specified day does not appear as the start property of any ExcludedOccurrence entity. One of the sub-predicates I am trying to use is therefore
NSPredicate *occurrenceIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"(ALL excludedOccurrences.start != %#))", today];
where today is a NSDate object for today including only the day, month and year components. All of the excluded occurrences start properties also include just the day, month and year components.
While this should fine, at least reading the documentation for Core Data and NSPredicate, I get the following error message:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unsupported predicate
If I use the equivalent predicate
NSPredicate *occurrenceIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"!(ANY excludedOccurrences.start == %#))", today];
no exception is thrown, however the code does not work as expected: the occurrence for today, which should not be excluded, is instead excluded.
I am not sure also how to test for the case excludedOccurrences == nil: the following predicate
NSPredicate *nilPredicate = [NSPredicate predicateWithFormat: #"(excludedOccurrences == nil)"];
causes at runtime the exception
to-many key not allowed here
However, since the excludedOccurrences relationship is optional, I also need to test if it is nil.
How do I deal with this? Thank you in advance.
To test for an empty relationship you should compare the count of the to-many key to zero.
[NSPredicate predicateWithFormat:#"excludedOccurrences.#count == 0"];
As for your subpredicates, be aware that you can only have one of either the ALL or ANY modifiers in your final predicate, although you can use that modifier multiple times throughout the predicate.
Not OK: ANY foo.bar = 1 AND ALL foo.baz = 2
OK: ANY foo.bar = 1 AND !(ANY foo.baz != 2)
with the help of you all, I finally managed to determine the correct predicate for my scenario.
It looks like that an NSDate object is handled as a double, however, the double is never something like 3.7, it is always like 3.0
Therefore, the following predicate correctly works in my tests:
NSPredicate *occurrenceIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"(excludedOccurrences.#count == 0 || (excludedOccurrences.#count > 0 && NONE excludedOccurrences.start == %#))",thisDate];
where thisDate is a NSDate object containing only the day, month and year components (as in the case of the start property of the ExcludedOccurrence entity.
Testing for an empty relationship is basically done using the #count aggregate operator, as suggested by some folks at Apple.
Again, thankyou very much for your help.
I still observe that the documentation is flawed in several parts (especially where it says that ALL works fine while, instead, it does not work at all).
So, to test for a non-empty relationship, this actually works:
[NSPredicate predicateWithFormat:#"relationship.#count != 0"]
The solution given by Ashley Clark crashes for me giving "to-many key not allowed here"
And in swift 2, something like:
request.predicate = NSPredicate(format: " relationship.#count != 0")