Querying Core Data with Predicates - iPhone - iphone

So I'm trying to fetch objects from core data. I have list of say 80 objects, and I want to be able to search through them using a UISearchBar. They are displayed in a table.
Using the apple documentation on predicates, I've put the following code in one
of the UISearchBar delegate methods.
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
if (self.searchBar.text !=nil)
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"name LIKE %#", self.searchBar.text];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
else
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"All"];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort(); // Fail
}
[self.tableView reloadData];
[searchBar resignFirstResponder];
[_shadeView setAlpha:0.0f];
}
If I type in the search field an exact match to the name property of one of those objects, the search works, and it repopulates the table with a single cell with the name of the object. If I don't search the name exact, I end up with no results.
Any Thoughts?

It seems as though iPhone doesn't like the LIKE operator. I replaced it with 'contains[cd]' and it works the way I want it to.

use contains[cd] instead of like, and change:
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"All"];
to:
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"1=1"];

Did you try it using MATCH and regular expressions? Just curious to see if LIKE is something that should be avoided on the iPhone or not...

In typical Core Data application one should delete NSF fetchedResultsController:
[NSFetchedResultsController deleteCacheWithName: [self.fetchedResultsController cacheName]];
Or else you'll get an exception (ideally) or you'll have strange behavior.

Related

How do I Add a searchbar with NSPredicate

I am getting an error when add the
-(void) statement.
"use of undeclared identifier "filterContentForSearchText"
//start of Predicate
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults = [recipes filteredArrayUsingPredicate:resultPredicate];
}
end of search
Have a look at this sample, made using NSPredicate if your array doesn't contain this kind of data, show us your data or array handling, which type of data you are having in your array.

searchDisplayController does not display results if the iphone language is non English

I have a searchDisplayController which works perfectly when searching for English and Arabic words using a method as follows:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSInteger)scope
{
NSString *query = self.searchDisplayController.searchBar.text;
if (query && query.length) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ClientName contains[cd] %#", query];
[searchResultController_.fetchRequest setPredicate:predicate];
}
NSError *error = nil;
if (![[self searchResultController] performFetch:&error]) {
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
}
However, this works fine if the iphone language is English, but if I change the iPhone language to Arabic (global settings) and tried to search in Arabic or English words the searchResultsController will not display results, why ?
p.s. when I put a static Arabic word in query like this:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ClientName contains[cd] %#", #"تجريب"]; the searchDisplayController will display the correct result of the arabic word تجريب
EDIT: I've tried to build the predicate in code like this :
NSExpression *clientNameEx=[NSExpression expressionForKeyPath:#"ClientName"];
NSExpression *aClientEx=[NSExpression expressionForConstantValue:query];
NSPredicate *predicate=[NSComparisonPredicate predicateWithLeftExpression:clientNameEx
rightExpression:aClientEx
modifier:NSDirectPredicateModifier
type:NSContainsPredicateOperatorType
options:0];
but to no avail ...
I've made test project with XCode 4, MasterDetail template, CoreData checkbox On and added SearchBarController to the default Master view, added UISearchBarDelegate delegate into the header, and searchBarTextDidEndEditing method to invoke search once done is pressed.
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar{
[NSFetchedResultsController deleteCacheWithName:nil];
self.fetchedResultsController = nil;
[self.tableView reloadData];
}
Last step was to add into fetchedResultsController:
NSString *query = self.searchDisplayController.searchBar.text;
if (query.length) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"timeStamp contains[cd] %#", query];
NSLog(#"predicate %#", [predicate description]);
[fetchRequest setPredicate:predicate];
}
Switched iPhone Simulator and tested it with iOS5 and Arabic settings. All searches where successful, searching for word "test" and word "تجريب" brings up results.
However, word "تجريب" was copy pasted from your question, as my arabic is little bit rusted :).I've also tried to make record in database with first letter on arabic keyboard and then to search for it and result shows up.

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 do I get Attributes from Core Data into an Array for - iPhone SDK

I'm trying to retrieve data from Core Data and put it into a Mutable Array
I have an Entity called 'Stock' and in Properties, attributes called : code, price & description...
How do I get the data stored in these attributes into a simple Mutable Array?
I've added this code...
NSMutableArray *array = [[NSMutableArray alloc]init];
[array addObject:[stock valueForKey:#"code"]];
and I get this error...
'-[NSCFArray insertObject:atIndex:]: attempt to insert nil'
I have a 'Managed Object Class' called 'Stock' and declared called stock. Am I missing something?
If I do this in the -cellForRowAtIndexPath...
Stock *stock1 = [fetchedResultsController objectAtIndexPath:indexPath];
array = [[NSMutableArray alloc] init];
[array addObject:stock1.code];
NSLog(#"Filtered List is? %#", array);
In the console I can see these 2 items
'The Filtered array is 810005'
'The Filtered array is 810007
'
What must I do to get these items(810005 & 810007) into an array set up in the -viewDidLoad method? Like it does in the -cellForRowAtIndexPath?
Update
Hi Marcus,
Finally got it working (well, 80%)
I put this in the -cellForRowAtIndexPath
Stock *product = nil;
if (tableView == self.searchDisplayController.searchResultsTableView)
{
filteredListContent = [NSMutableArray arrayWithObjects:stock1.code, nil];
product = [self.filteredListContent objectAtIndex:indexPath.row];
[self configureFilteredCell:cell atIndexPath:indexPath];
[filteredListContent objectAtIndex:indexPath.row];
NSLog(#"Filtered List Array List is? %#", stock1.code);
}
else
{
listContent = [NSMutableArray arrayWithObjects:stock1.code, nil];
[self configureCell:cell atIndexPath:indexPath];
NSLog(#"List Array List is? %#", stock1.code);
}
Then I used this code in the scope
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
self.savedSearchTerm = searchText;
if (searchText !=nil)
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"code beginsWith[cd] %#", searchText];
[fetchedResultsController.fetchRequest setPredicate:predicate];
}
else
{
NSPredicate *predicate =[NSPredicate predicateWithFormat:#"code contains[cd] %#", searchText];
[fetchedResultsController.fetchRequest setPredicate:predicate];
[self.tableView reloadData];
}
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error])
{
// Handle error
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
exit(-1); // Fail
}
[self.tableView reloadData];
Everything is filtering fine but when I hit cancel on the search, it's not reloading the original data...
I won't be defeated...!!
Thanx
Since you are having this issue in your -viewDidLoad, I am guessing (and without the code from -viewDidLoad, it is only a guess) that you are trying to fetch objects from the NSFetchedResultsController before the -executeFetch: has been called on the controller and therefore you are in the land of nils.
I would suggest setting a break point in your -viewDidLoad and watching the values and you walk through your code. This will tell you what is nil and where.
Of course a better question is, why are you trying to put NSManagedObject instances into a NSMutableArray? Since they are already in your NSFetchedResultsController is there really a need to build up another array? What is the end goal?
Update
Now I understand what you are trying to do.
Solution 1
Only populate the array when a search has been conducted. Take a look at the http://developer.apple.com/iphone/library/samplecode/TableSearch/index.html example code and you should see how to apply it to your situation.
If you want to enter the table view with a pre-defined search then you need to perform it after you have executed a -performFetch: in the NSFetchedResultsController.
Solution 2
Modify the NSPredicate on the NSFetchedResultsController to include your search terms and then execute -performFetch: on the NSFetchedResultsController, you may have to do a -reloadData on the table as well, I am not sure.
When the user clears the search field you reset the predicate and re-fetch everything. Since it is all cached there should be no performance penalty.
Solution 2 just occurred to me and I have not tested it personally but there is no reason it shouldn't work just fine. Should even give you live updates within the search.
Have you read the documentation? You fetch your Stock instances (all of them or filter them with a predicate), then do with them whatever you please.
You can then add their properties to an array individually:
[array addObject:[stockInstance valueForKey:#"price"];
... or use a combination of < NSKeyValueCoding > protocol methods such as -dictionaryWithValuesForKeys: NSDictionary methods such as -objectsForKeys:notFoundMarker: to get an array for given keys.
This may or may not actually be what you need to do, though. It depends on what you intend to use the resulting array for. If you want a quick sum of all matching Stock instances' "price" values, for example, you can use Set and Array Operators. It really depends on what you're trying to achieve.
When I got your error,
'-[NSCFArray insertObject:atIndex:]: attempt to insert nil'
I had given the fetchedRequest a sort descriptor that had a nil key. The error appeared when I used these lines:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:nil ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
The error disappeared when I set the key to #"name":
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];