Core Data NSPredicate checking for BOOL value - iphone

I am currently having an issue pulling all data from db whereby i.e 1 parameter is TRUE.
I am using NSPredicate and below is a sample code
NSManagedObjectContext *context = managedObjectContext_;
if (!context) {
// Handle the error.
NSLog(#"ERROR CONTEXT IS NIL");
}
NSEntityDescription *entity = [NSEntityDescription entityForName:#"tblcontent" inManagedObjectContext:managedObjectContext_];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"bookmarked == YES"];
[request setPredicate:predicate];
I tried setting predicatewithformat to almost everything but it still does not pull out bookmarks which have a YES value.
I even tried (#"bookmarked == %d",YES) but with not luck. I don't want to have to get the whole array and then filter it manually by doing if(object.bookmarked == YES) .....blabla.
I will really appreciate some help.
Many thanks.

Based on Apple Document Here, we can use the following two methods to compare Boolean:
NSPredicate *newPredicate = [NSPredicate predicateWithFormat:#"anAttribute == %#",[NSNumber numberWithBool:aBool]];
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == YES"];
However, the above predicate cannot get out the ones with empty anAttribute. To deal with an empty attribute, you need the following method according to Apple document here:
predicate = [NSPredicate predicateWithFormat:#"firstName = nil"]; // it's in the document
or
predicate = [NSPredicate predicateWithFormat:#"firstName == nil"]; // == and = are interchangeable here

Sneaking in with the Swift 3/4 answer:
let predicate = NSPredicate(format: "boolAttribute == %#", NSNumber(value: true))
We have to use NSNumber apparently because a literal bool is not acceptable per Apple.
Stolen from here ;)

For some reason, Flow's solution would not work for me:
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == YES"];
However, this did:
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == 1"];

I'm late to the party and as discussed using 0 and 1's is the way to go however there is a better way to show this by using NSNumber BOOL literals like #YES or #NO. It converts it to a 1 or a 0 but is more visually friendly.
NSPredicate *testForTrue = [NSPredicate predicateWithFormat:#"anAttribute == %#", #NO];

Core Data entities does not have any default values when you create attributes for Entity, so to make your predicate work you should either set default values for boolean attributes or use predicate in this way.
If you provide default value (NO or YES) for any boolean property of Entity then use predicate like this
[NSPredicate predicateWithFormat:#"boolAttribute == %#", #NO];
[NSPredicate predicateWithFormat:#"boolAttribute == NO", #NO];
[NSPredicate predicateWithFormat:#"boolAttribute == %0"];
If you do not have default values or some of Entities was already created without default values then to filter by false value use this sentence:
[NSPredicate predicateWithFormat:#"boolAttribute == %# || boolAttribute == nil", #NO];

Swift:
fetchRequest.predicate = NSPredicate(format: "%K == NO",#keyPath(MyEntity.isOccupied))
or
fetchRequest.predicate = NSPredicate(format: "%K == NO",\MyEntity.isOccupied)

You haven't mentioned what results you're getting. Two things that are missing from your code listing is where you set your request's entity and where you actually ask the context to execute the fetch request. I'd start there.

For me, it happened coz of assign value in Object is wrong when saving.
You have to save like this
YourNSMNanagedObject.visibleBoolean = [[NSNumber alloc] initWithBool:false]
Then all predicate will work when fetching.
E.g.
// Filter only unvisible item
NSPredicate *favouriteFilter = [NSPredicate predicateWithFormat:#"visibleBoolean==NO"];

I keep getting bit by this one and it's not always clear that booleans in core data get saved as NSNumbers.
For the most part I make sure when creating entities I set #NO for any Boolean properties but there's always the case where I spend a whole bunch of time trying to figure out why a test doesn't pass when I've forgot to set #NO on entity creation.
This might not be the best choice for clear code but I've now started to always use != predicates for booleans in core data like (isCompleted != YES) instead of (isCompleted == NO). The fact nil == false pattern is true everywhere else but not in core data predicates and properties can be very confusing.

Related

Correct way to create a NSPredicate

i found 2 different ways to create a NSPredicate.
Way 1:
NSExpression *exprName = [NSExpression expressionForKeyPath:#"name"];
NSExpression *exprFilter = [NSExpression expressionForConstantValue: name ];
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression: exprName
rightExpression: exprFilter
modifier: NSDirectPredicateModifier
type: NSContainsPredicateOperatorType
options: NSCaseInsensitivePredicateOption];
Way 2:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name CONTAINS[c] %#", name];
What is the best way to create a NSPredicate and also prevent SQLInjection?
The first way is useful if you are setting up a complex predicate programmatically. Other than that, the second way is fine. You don't have to worry about SQL injection with Core Data.
relevant
also relevant

Is there a more efficient alternative to find an object with a to-one relationship to two objects I have?

I have the following Core Data setup:
Project has-many Answer
Field has-many Answer
Answer has-one Field
Answer has-one Project
I need to find the Answer for each Field that is also owned by Project. I'm currently using a predicate for this and executing a fetch request:
NSEntityDescription *answerEntity = [NSEntityDescription entityForName:#"Answer" inManagedObjectContext:self.managedObjectContext];
NSPredicate *answerPredicate = [NSPredicate predicateWithFormat:#"ANY project == %# && field == %#", self.project, self.field];
NSFetchRequest *answerRequest = [[NSFetchRequest alloc] init];
[answerRequest setEntity:answerEntity];
[answerRequest setPredicate:answerPredicate];
NSError *error = nil;
NSArray *predicates = [self.managedObjectContext executeFetchRequest:answerRequest error:&error];
I'm still new to Core Data but I believe the fetchRequest is querying the database each time I call it, is there a more efficient way of finding these Answer objects?
If I understand correctly, you already have a Field object and an Project object and you want to find the Answer objects they have in common.
If so, the solution is a simple intersect set operation:
NSSet *answersInCommon=[[aFieldObj mutableSetValueForKey:#"answers"] intersectSet:[aProjectObj mutableSetValueForKey:#"answers"]];
... which will return only those Answer objects that appear in both relationships.
Update:
#pdenya in comment provides an enhancement :
Just want to clarify a minor error and a small point that makes this less than ideal. intersectSet returns (void) so the syntax for this would be:
NSMutableSet *answers=[field mutableSetValueForKey:#"answers"];
[answers intersectSet:[project mutableSetValueForKey:#"answers"]];
This solution also modifies the aFieldObj.answers array meaning you can't use this while iterating. setWithSet clears this right up. Example:
NSMutableSet *answers = [NSMutableSet setWithSet:[project mutableSetValueForKey:#"answers"]];
[answers intersectSet:[field mutableSetValueForKey:#"answers"]];
#pdenya's is the correct form.
Best alternate method I've found so far:
NSMutableSet *answers = [self.project mutableSetValueForKey:#"answers"];
NSPredicate *answerPredicate = [NSPredicate predicateWithFormat:#"field == %#", field];
[answers filterUsingPredicate:answerPredicate];
NSManagedObject *answer = nil;
if([answers count] > 0) {
answer = [[answers allObjects] objectAtIndex:0];
}

CoreData Basics Help

EDIT:
I have altered the NSPredicate as recommended so that my fetch code look like so. Having printed to the UITextView like this, when I press load it spits out the following:
<NSManagedObject: 0x1c7cf0>(entity: DatedText; id: 0x1420c0 <x - coredata://B52D4F88-0210-4AE2-9DA6-C05ED64FE389/DatedText/p12> ; data: <fault>)
So either its not getting any data data because it hasn't been saved/loaded correctly or I am trying to get the loaded result into a UITextView the incorrect way. Any ideas?
NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
NSEntityDescription *testEntity = [NSEntityDescription entityForName:#"DatedText" inManagedObjectContext:self.managedObjectContext];
[fetch setEntity:testEntity];
NSPredicate *pred = [NSPredicate predicateWithFormat:#"dateSaved == %#", datePicker.date];
[fetch setPredicate:pred];
NSError *fetchError = nil;
NSArray *fetchedObjs = [self.managedObjectContext executeFetchRequest:fetch error:&fetchError];
if (fetchError != nil) {
NSLog(#" fetchError=%#,details=%#",fetchError,fetchError.userInfo);
return nil;
}
NSString *object = [NSString stringWithFormat:#"%#",[fetchedObjs objectAtIndex:0]];
noteTextView.text = object;
I have been having all sorts of problems working out how to use Core Data, so I have gone back to basics, new window based ipad project using core data.
I have added a view and some code which doesn't work, hehe. Im basically trying to save some text to a date, then when going back to that date, the text which was previously saved will be shown again.
There's a tutorial on iPhone developer site here. And there are several sample codes with Core Data as well. These should get you started.
I checked your project and aside from having to synthesize the CoreData properties, I also just noticed you were trying to assign an NSArray to your fetch predicate, but it actually expects an NSPredicate object. You should use this instead:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(dateSaved == %#)", datePicker.date];
[fetch setPredicate:pred];
If you want to set more than 1 predicate you should do that on your predicate string i.e.
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(dateSaved == %#) AND (dateSaved <= %#", datePicker.date, [NSDate date]];
Cheers,
Rog
You most likely crashing because your ivar is managedObjectContext_ but you are using self.managedObjectContext. You also need to synthesize the core data ivars even if you provide a custom getter.
You're setting your NSFetchRequest's predicate to an NSArray, not an NSPredicate.
If you had posted the actual crash, it would probably say something like an unknown selector was sent to an instance of NSArray.

iphone Core Data - Filtering NSFetchedResultController?

I was given a framework written by other programmers to access the core data.
In this situation i receive a pre-loaded NSFetchedResultController which I need to filter, to display a part of it's data.
Here is what I tried:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category==%#", #"current"];
[NSFetchedResultsController deleteCacheWithName:#"Root"];
[myResultController.fetchRequest setPredicate:predicate];
myResultController.fetchedObjects = [myResultController.fetchedObjects filteredArrayUsingPredicate:predicate];
And i get an error saying that object cannot be set, either setter method is missing, or object is read-only.
So whats the best way to filter an NSFetchResultController which is already loaded, without having to store the filtered data into another array?
fetchedObjects is readonly. You cannot set it manualy.
What you need to do is to perform a new fetch with your myResultController.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category==%#", #"current"];
[NSFetchedResultsController deleteCacheWithName:#"Root"];
[myResultController.fetchRequest setPredicate:predicate];
//myResultController.fetchedObjects = [myResultController.fetchedObjects filteredArrayUsingPredicate:predicate];
[myResultController performFetch:nil];

How to correctly setup a NSPredicate for a to-many relationship when using Core Data?

I have a Core Data model in which a Task entity includes an optional to-many relationship ExcludedDays to the ExcludedDay entity. One of the properties of ExcludedDay is day, which is an NSDate object. The ExcludedDay entity has an inverse mandatory to-one relationship to the Task entity.
In order to fetch the tasks for a specified day, I need to make sure that the specified day does not appear as the day property of any ExludedDay entity.
I started trying
NSPredicate *dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"ALL excludedDays.day != %#", today];
However, despite what the documentation says, ALL does not work and the application throws an exception: Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘Unsupported predicate.
After posting the same question in this forum, I was able to devise the following predicate with the help of various people:
NSPredicate * dayIsNotExcludedPredicate = [NSPredicate predicateWithFormat: #"excludedDays.#count == 0 || (excludedDays.#count > 0 && NONE excludedDays.day == %#))", today];
While this worked at first, I have just discovered that this only works when the ExcludedDay entity contains ONLY one day. As soon as the ExcludedDay entity contains more than one day for the same task, this predicate stops working. As a result, a task is selected for a day even though the day appears as a day in the ExcludedDay entity, which is of course wrong. The problem is not tied to the property day being a NSDate object: replacing day with the corresponding NSString or equivalently with an integer, I still face the same issue and incorrect behaviour.
What is the correct way to implement the predicate in this case? May this be a bug related to the ANY aggregate operator when using core data?
Thank you in advance, this is now driving me crazy.
It turns out this is yet another problem with missing and/or inconsistent documentation.
The correct predicate in this case is the following one:
[NSPredicate predicateWithFormat:#"(excludedOccurrences.#count == 0) OR (0 == SUBQUERY(excludedOccurrences, $sub, $sub.day == %#).#count)", today]
In the predicate, a subquery is used to test if the number of related excludedOccurrences with a date matching your test date is equal to zero. However, the documentation is misleading. Here is what the Predicate Programming Guide says regarding the use of predicates in conjunction with to-many relationships.
Using Predicates with Relationships
If you use a to-many relationship, the construction of a predicate is slightly different. If you want to fetch Departments in which at least one of the employees has the first name "Matthew," for instance, you use an ANY operator as shown in the following example:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ANY employees.firstName like 'Matthew'"];
If you want to find Departments in which all the employees are paid more than a certain amount, you use an ALL operator as shown in the following example:
float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"ALL employees.salary > %f", salary];
Quite curious how to solve this I setup a little project and created the context you are using.
NSDate *today = [NSDate date];
NSMutableArray *objects = [NSMutableArray array];
{
NSArray *day = [NSArray arrayWithObjects:today, [today dateByAddingTimeInterval:20.0f], nil];
NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:#"day"];
NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:#"excludedDay"];
[objects addObject:object];
}
{
NSArray *day = [NSArray arrayWithObjects:[today dateByAddingTimeInterval:20.0f], nil];
NSDictionary *excludedDay = [NSDictionary dictionaryWithObject:day forKey:#"day"];
NSDictionary *object = [NSDictionary dictionaryWithObject:excludedDay forKey:#"excludedDay"];
[objects addObject:object];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"NONE excludedDay.day == %#", today];
NSArray *filtered = [objects filteredArrayUsingPredicate:predicate];
NSLog(#"%#", filtered);
This gives the object when:
The day array is empty
The day array does not contain the 'today' date
This does not give the object when:
The day array contains the 'today'
It doesn't matter how many objects in the day array are