Predicate Problems - ios

I have a managed object with the field of journalHeaderID. I can see via NSLog that those items are properly being placed into Core Data. To display the data I need to filter by what is relevant to the view.
So as I'm configuring my view of this data I have created an array of IDs that are relevant. When I run the code, though, I get no results even though I know they are there.
I thought this predicate was the right one to use, but it's not working right and I am not sure why.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"journalHeaderID IN %#", idsToList];
Here's the array as it passees into the predicate, and I've tried it as both NSNumber and NSString.
<__NSArrayM 0xa01af70>(
800555,
800552,
800685,
803592,
803593
)
NSFetchRequest *request = [NSFetchRequest new];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"JournalHeader" inManagedObjectContext:[[BWCoreDataHelper sharedInstance] managedObjectContext]];
[request setEntity:entity];
[request setFetchBatchSize:20];
if( idsToList.count > 0 ) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"journalHeaderID IN %#", idsToList];
[request setPredicate:predicate];
}
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"headerCreateDate" ascending:NO];
[request setSortDescriptors:#[sort]];
NSFetchedResultsController *fetchedResults = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:[[BWCoreDataHelper sharedInstance] managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
self.fetchedResultsController = fetchedResults;
self.fetchedResultsController.delegate = self;

From the lack of information on what results you are getting, or even if you get the delegate message, I have one suggestion.
Call - (void)performFetch:(NSFetchRequest *)fetch on the NSFetchedResultsController with your created fetch.
If you're already doing this, then it isn't obvious.

Related

Fetching contacts based on tag

I'm trying to modify a simple Core Data fetch request for contacts to only look for contacts with a relationship with a certain tag. Contact and Tag are both entities with a many-to-many relationship.
I understand with Core Data I can do this by first fetching the Tag object, and then calling tag.contact, but I don't want to do it this way as the rest of the code is dependent on the fact that the fetchResultsController returns Contact objects, not Tag objects.
If I were to do relational databasing, I could do a simple cross-table query and find all contacts with a certain tag. Is there a simple way I can replicate this via Core Data?
-(NSFetchedResultsController *) fetchedResultsController {
//if fetch controller already exists
if(_fetchedResultsController != nil) {
return _fetchedResultsController;
}
//create a new fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Contact"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"lastName"
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
//instantiate the fetch controller with the fetch request and sort by last name into sections
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self managedObjectContext] sectionNameKeyPath:nil cacheName:nil];
//declare delegate of fetch controller as self
_fetchedResultsController.delegate = self;
NSLog(#"fetchResultsController Created");
return _fetchedResultsController;
}
Use NSPredicate.
Lets say you have related Contacts with Tag by name tags and tag entity has property name.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY tags.name = [cd] %#", #"sales"];
[fetchRequest setPredicate:predicate];

Core Data fetchedresultscontroller returns empty on device only

I Wrote an application that uses core data and it was working fine, on both simulator and devices. Then I made a new git branch of the project and it works perfectly on the simulator but not on the devices.
Here is the code for the fetchedResultsController
-(NSFetchedResultsController *)fetchedResultsController{
if (_fetchedResultsController != nil) {
NSLog(#"Fetched Controler : %#", _fetchedResultsController);
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Membership" inManagedObjectContext:managedObjectContext];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userdata.email == %#", [[NSUserDefaults standardUserDefaults] stringForKey:#"email"]];
NSSortDescriptor *sort1 = [[NSSortDescriptor alloc]initWithKey:#"type" ascending:NO];
NSSortDescriptor *sort2 = [[NSSortDescriptor alloc]initWithKey:#"membership_name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:sort1,sort2, nil];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"type" cacheName:nil];
return _fetchedResultsController;
}
Then I fetch the objects in view will apear.
-(void)viewWillAppear:(BOOL)animated{
NSError *error;
[[self fetchedResultsController]performFetch:&error];
[self.tableView reloadData];
}
The managed object context is passed from appDelegate to the login view then from the loginView to the second view. On the second view is where I am having problems
The only thing that I can think of is that something is happening with the memory limitations on the device or maybe a concurrency issue?
If there are are any threads created I did not mean to do it.
It seems that your predicate
NSPredicate *predicate =
[NSPredicate predicateWithFormat:#"userdata.email == %#",
[[NSUserDefaults standardUserDefaults] stringForKey:#"email"]];
is returning an empty set. Log the value of this user default and I am convinced this will be cleared up.

Core Data and Table Views

Scenario :
I have an expense tracking iOS Application and I am storing expenses from a expense detail view controller into a table view that shows the list of expenses along with the category and amount.
On the top of the tableview, is a UIView with CALENDAR button, a UILabel text showing the date (for example: Oct 23, 2012 (Sun)) and 2 more buttons on the side.
The pressing of the calendar button opens up a custom calendar with the current date and the two buttons are for decrementing and incrementing the date correspondingly.
I want to save the expenses according to the date which is an attribute in my Core data entity "Expense".
Question: Suppose I press the calendar button and choose some random date from there, the table view underneath it, should show that day's particular expenses. What I mean is I want the table view to just show a particular date's expenses and if I press the button for incrementing the date or decrementing the date, the table view should show that day's expenses. I am using NSFetchedResultsController and Core Data in order to save my expenses.
Any thoughts on how I would achieve this? Here's the code for FRC.
-(NSFetchedResultsController *)fetchedResultsController
{
if(_fetchedResultsController != nil)
{
return _fetchedResultsController;
}
AppDelegate * applicationDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
NSManagedObjectContext * context = [applicationDelegate managedObjectContext];
NSFetchRequest * request = [[NSFetchRequest alloc]init];
[request setEntity:[NSEntityDescription entityForName:#"Money" inManagedObjectContext:context]];
NSSortDescriptor *sortDescriptor1 =
[[NSSortDescriptor alloc] initWithKey:#"rowNumber"
ascending:YES];
NSArray * descriptors = [NSArray arrayWithObjects:sortDescriptor1, nil];
[request setSortDescriptors: descriptors];
[request setResultType: NSManagedObjectResultType];
[request setIncludesSubentities:YES];
[sortDescriptor1 release];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController.delegate = self;
[request release];
NSError *anyError = nil;
if(![_fetchedResultsController performFetch:&anyError])
{
NSLog(#"error fetching:%#", anyError);
}
return _fetchedResultsController;
}
Thanks guys.
You would have to create a new NSFetchedResultsController with a new NSFetchRequest that has an appropriately set NSPredicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(date == %#)", dateToFilterFor];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Expense" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
// ...
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"SomeCacheName"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
Don't forget to call [self.tableView reloadData]; after assigning the new FRC.
Edit:
You can assign a predicate to an NSFetchRequest which then is assigned to the fetchedResultsController. You can think of the predicate as a filter.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(date == %#)", dateToFilterFor];
If you add this to the fetch request by calling [fetchRequest setPredicate:predicate]; you tell the fetched request to only fetch results where to date property of the NSManagedObject matches the date you provide in the predicate. Which is exactly what you want here.
So if you have a method that's called after the user selected a date you could modify it like this:
- (void)userDidSelectDate:(NSDate *)date
{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
//Here you create the predicate that filters the results to only show the ones with the selected date
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(date == %#)", date];
[fetchRequest setPredicate:predicate];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"timeStamp" ascending:NO];
NSArray *sortDescriptors = #[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:#"Master"];
aFetchedResultsController.delegate = self;
//Here you replace the old FRC by this newly created
self.fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
//Finally you tell the tableView to reload it's data, it will then ask your NEW FRC for the new data
[self.tableView reloadData];
}
Notice that if you're not using ARC (which you should) you'd have to release the allocated objects appropriately.

NSPredicate predicateWithFormat with object that changes over time

I have this working code, but now i need to be able to change the NSPredicate, based on the object used in the predicateWithFormat as follows:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Item" inManagedObjectContext:_context];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"self.atrObj == %#", _currentUser.atrObj.objectID]];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortContent = [[NSSortDescriptor alloc] initWithKey:#"date" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortContent]];
[fetchRequest setFetchBatchSize:10];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:_context sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController = theFetchedResultsController;
self.fetchedResultsController.delegate = self;
I need the fetchedResultsControlelr to use the new _currentUser object when i change the _currentUser object from the app delegate which has this tableviewcontroller a property.
[self.tableViewcontroller setCurrentUser:user];
Thank you!
You can change the predicate anytime but be sure to re-fetch via performFetch. Also from Apple docs:
Important: Important If you are using a cache, you must call deleteCacheWithName: before changing any of the fetch request, its
predicate, or its sort descriptors. You must not reuse the same
fetched results controller for multiple queries unless you set the
cacheName to nil.
Also, from the Apple docs of NSFetchedResultsController:
Modifying the Fetch Request You cannot simply change the fetch request
to modify the results. If you want to change the fetch request, you
must:
If you are using a cache, delete it (using deleteCacheWithName:).
Typically you should not use a cache if you are changing the fetch
request.
Change the fetch request.
Invoke performFetch:.
Whenever the value of currentUser changes, do this (perhaps inside the setter):
NSError *error = nil;
self.fetchedResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:#"atrObj == %#", self.currentUser.atrObj];
if(![self.fetchedResultsController performFetch:&error]) {
// Handle errors
}

Sorting nsfetchedresultscontroller

I have this structure in a entity of core data:
ID Region SubRegion SubRegionID CellText
1 Face Mouth 2 something
2 Face Eyes 1 ...
3 Face Nose 4 ...
...
But I want my tableview (nsfetchedresultscontroller) sorted this way:
Inside each section: cells sorted by ID property
Sections in tableView: SubRegion names but sorted by SubRegionID.
How can I achieve this? By now I'm doing this but I don't know is the best practice to sort section titles by subregionID:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"DataBase" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(Region like '%#')", #"Face"]];
[fetchRequest setPredicate:requestPredicate];
NSSortDescriptor *ID = [[NSSortDescriptor alloc]
initWithKey:#"ID" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects: ID, nil]];
[fetchRequest setFetchBatchSize:10];
fetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:context sectionNameKeyPath:#"SubRegion"
cacheName:nil];
fetchedResultsController.delegate = self;
Thanks for your time!
There is some redundancy in your predicate. Rather than
NSPredicate *requestPredicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"(Region like '%#')", #"Face"]];
it should be just
NSPredicate *requestPredicate =
[NSPredicate predicateWithFormat:#"%K == %#", #"Region", #"Face"];
It might also have to do with how you set up your NSFetchedResultsController. You should set the sectionNameKeyPath as "SubRegionID", not "SubRegion". In your tableView datasource method titleForSection: you should then return the name of the subregion based on the ID.

Resources