CoreData sort by upcoming birthdays - iphone

I'm building my first app in xcode and trying to fetch a list of people ordered by upcoming birthdays using coreData and NSFetchedResultController. My entity is setup as follows
uBirthdays
--------------
NSString uName
NSDate uBday
Here is my current code:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"uBirthdays" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"uBday" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"sectionNameGen" cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
[sort release];
[fetchRequest release];
[theFetchedResultsController release];
return _fetchedResultsController;
}
Is it possibly to sort that by upcoming birthdays? or would I have to de-normalize my birthdays so I can sort by month and day only? Any input is greatly appreciated.
Thank you

Here's what you can do.
1) Set NSSortDescriptors for your uBDay and uName so your records will be sorted first by birthday, then by name.
2) Create a NSPredicate that pulls all birthdays between [NSDate now] (i.e. today) up to a certain time in the future (i.e. 30 days into the future = [NSDate dateWithTimeIntervalSinceNow:kOneDayTimeInterval*30])
Example code below:
#define kOneDayTimeInterval 86400 // this is in seconds: 86400 = 1 day
- (NSFetchedResultsController *)fetchedResultsController {
...
NSSortDescriptor *sortByBDay = [[NSSortDescriptor alloc] initWithKey:#"uBday" ascending:YES];
NSSortDescriptor *sortByName = [[NSSortDescriptor alloc] initWithKey:#"uName" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortByBDay,sortByName, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortByName release];
[sortByReleaseDate release];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(uBDay > %#) AND (uBDay <= %#)", [NSDate date], [NSDate dateWithTimeIntervalSinceNow:kOneDayTimeInterval*30]];
[fetchRequest setPredicate:predicate];
...

Related

NSFetchResultsController delegate not getting called

I have the following code:
- (NSFetchedResultsController *)fetchedResultsController {
// Set up the fetched results controller if needed.
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DiskStory" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"isRemoved == %#", [NSNumber numberWithBool:NO]];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"created" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Set limit
[fetchRequest setFetchBatchSize:25];
// Set batch size
[fetchRequest setFetchLimit:50];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:kSavedStoryCache];
aFetchedResultsController.delegate = self;
fetchedResultsController = aFetchedResultsController;
}
return fetchedResultsController;
}
in my viewDidLoad I have:
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
I was wondering why my :
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
}
delegate, isn't called? I put a break point inside it. Any idea?
I had this problem before, it's not called when you call performFetch for the first time but only when data in fetchedResultsController is actually changed. For instance when you delete object from context that is in this fetch results, delegate will be called.
I think you have used self. at wrong places. Please see the code below; i have added comments to the changes
- (NSFetchedResultsController *)fetchedResultsController {
// Set up the fetched results controller if needed.
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"DiskStory" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"isRemoved == %#", [NSNumber numberWithBool:NO]];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"created" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Set limit
[fetchRequest setFetchBatchSize:25];
// Set batch size
[fetchRequest setFetchLimit:50];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:kSavedStoryCache];
aFetchedResultsController.delegate = self;
fetchedResultsController = aFetchedResultsController; // -> Shouldn't be self.fetchedResultsController
}
return fetchedResultsController; // -> Shouldn't be self.fetchedResultsController
}
Also, if you have synthesize your fetchedResultsController as follows:
fetchedResultsController = _fetchedResultsController
Then in - (NSFetchedResultsController *)fetchedResultsController method you should refer to it as _fetchedResultsController

NSPredicate in NSFetchedResultsController throwing EXC_BAD_ACCESS

I have a UITableViewController using an NSFetchedResultsController. If I remove the predicate, It works fine, but with the predicate, it throws bad access (EXC_BAD_ACCESS). I enabled NSZombieEnabled, but that turned up nothing. I'm using ARC. I don't get it! Please help. Here's my fetchedresultscontroller code NOTE: Changing my predicate to random simple predicates such as "unitNumber = nil" or "unitNumber LIKE %#", unString STILL CAUSES a crash, however commenting the predicate out entirely will not produce crash (But then I have all objects and no filter obviously):
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Unit" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"workTicket.appointment.scheduleRowPointer = %#", rp];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
// Sort Descriptor
NSSortDescriptor *unitNumberDescriptor = [[NSSortDescriptor alloc] initWithKey:#"unitNumber" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:unitNumberDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"addedToTicket" cacheName:nil];
return _fetchedResultsController;
}
Change:
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
to :
[fetchRequest setPredicate:predicate];

tableView reloadData does not work

I have a tableview in a tab bar application.
I am loading the data in viewDidLoad
managedObjectContext = nil;
managedObjectContext = [(RecipesAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"BrilliantMustache" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"badge.length > 0"];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"badge" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1,sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"badge" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor1 release];
[sortDescriptor2 release];
[sortDescriptors release];
I am attempting to reload the data in viewWillAppear
- (void)viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
}
In this view, I call another view, which when changed, updated the core data when released. When I navigate back to the tableview, it does not update. The only way it will update, is if I quit the app and reload from scratch.
In total there are 2 separate table views. The strange thing is, the first tableview DOES refresh.
Any thoughts?
Rob
You need to do what you do in viewDidLoad in viewWillAppear. viewDidLoad is only called at startup, so despite changing the data, your view never changes its own local data, so when you call reloadData it still uses the old data.
Edit:
What you should have:
- (void)viewWillAppear:(BOOL)animated {
managedObjectContext = nil;
managedObjectContext = [(RecipesAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"BrilliantMustache" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"badge.length > 0"];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"badge" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1,sortDescriptor2, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:#"badge" cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor1 release];
[sortDescriptor2 release];
[sortDescriptors release];
[self.tableView reloadData];
[super viewWillAppear:animated];
}
- (void)viewDidLoad:(BOOL)animated {
[super viewDidLoad:animated];
}
Have you implemented the NSFetchedResultsControllerDelegate methods properly? The FRC should be handling updating the table when it receives a notification of changes from the managed object context...
Edit: NSFetchedResultsController Class Reference

Core data with primary key

I want to retrieve data from core data (sqlite database) according to primary key. it is displaying data in ascending or descending order.. ordering of data is not like database..
my code is:
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Tutorial" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Topic" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
your code says
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"Topic" ascending:NO];
so your result will be sorted by the field "Topic" in descending order
maybe
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"id" ascending:YES];
don't think you should be using "id" in coredata unless u are migrating from some legacy database

Sorting Core Data with Predicate to eliminate duplicates

I have an Event database loaded into Core Data that has duplicate Event titles. This has been made so the database can provide unique information for each day of the event. Eg fluctuations in pricing on each date.
I now need to remove the duplicate event titles from a list that will be displayed as table view with NSFetchRequest and NSPredicate to provide the filter. But all the examples I've seen require a none dynamic key value to be used as a target for the predicate filter. eg below NSDate provides the time now as a key filter and it works.
Currently NSString * title targets a value in the events ManagedObject class that returns a nil value. Here is a snip from the FetchResultsController.
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController == nil) {
NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
NSPredicate *predicate = [[[NSPredicate alloc] init] autorelease];
[fetchRequest setReturnsObjectsAsFaults:NO];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Event" inManagedObjectContext:managedObjectContext]];
NSArray *sortDescriptors = nil;
NSString *sectionNameKeyPath = nil;
NSDate *date = [NSDate date];
NSString *title = [events title];
if ([fetchSectioningControl selectedSegmentIndex] == 1) {
predicate = [NSPredicate predicateWithFormat:#"(closeDate >= %#) AND (title == %#)", date, title ];
sortDescriptors = [NSArray arrayWithObjects:[[[NSSortDescriptor alloc] initWithKey:#"category.name" ascending:YES] autorelease], [[[NSSortDescriptor alloc] initWithKey:#"openDate" ascending:YES] autorelease], nil];
sectionNameKeyPath = #"category.name";
} else if ([fetchSectioningControl selectedSegmentIndex] == 0){
predicate = [NSPredicate predicateWithFormat:#"closeDate >= %#", date];
sortDescriptors = [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:#"openDate" ascending:YES selector:#selector(compare:)] autorelease]];
sectionNameKeyPath = #"day";
}
[fetchRequest setPredicate:predicate];
[fetchRequest setSortDescriptors:sortDescriptors];
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:sectionNameKeyPath cacheName:#"EventsCache"];
}
return fetchedResultsController;
}
You could set
setReturnsDistinctResults:YES
on your fetchRequest.
For more, see the docs:
NSFetchRequest Class Reference