amitsbajaj
amitsbajaj

Reputation: 1304

Filter Table View Controller By Just the Year in an NSDate Attribute Using Core Data

I have a requirement which I am sure is possible, but I just have no idea how to achieve it and any assistance would be appreciated.

I have a simple two-tabbed table view controller. The user presses the plus button on the navigation bar and is taken modally to another view controller to add a new transaction. The user adds text into textfields and select a date from the UIDatePicker. The user presses save and this is saved to Core Data and displayed in the first tab Table view in chronological order with the dates making up the sections.

The second tab is supposed to display just the years from the transactions in the cells. So no sections here, but a table view displaying just the Years where entries exist, like 2013, 2012, 2011, etc. For example, if you created 20 entries with the year being 2013, then 2013 should be displayed ONCE in this date table view and when you click on 2013 you're taken to all of those entries, etc. If you don't have any entries with the year 2012, then you're not going to see that.

I am stuck on how to implement this dates table view.

I am using NSFetchedResultsController so with fetchRequests and predicates, this should be achievable.

The model of the application is: Transaction Entity Occasion Entity Date Entity Person Entity

The transaction Entity has a relationship to the Occasion, Person and Date Entity.

Let's look at some code:

The save method in the modal view controller:

- (IBAction)save:(id)sender
{
    NSManagedObjectContext *context = [self managedObjectContext];
    Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:@"Transaction" inManagedObjectContext:context];
    Item *itemType = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:context];

    Person *enteredPerson = (Person *)[Person personWithName:self.nameTextField.text inManagedObjectContext:context];
    transaction.whoBy = enteredPerson;

    Occasion *enteredOccasion = (Occasion *)[Occasion occasionWithTitle:self.occasionTextField.text inManagedObjectContext:context];
    transaction.occasion = enteredOccasion;

    Date *date = (Date *)[Date occasionWithDate:self.datePicker.date inManagedObjectContext:context];
    transaction.dates = date;

}

The Date object calls a category on the Date NSManagedObject subclass with the method occasionWithDate to check whether the existing date already exists. I don't think the duplicated functionality is working yet, but the question is below. The method called is:

+ (Date *)occasionWithDate:(NSDate *)enteredDate inManagedObjectContext:(NSManagedObjectContext *)context
{
    Date *date = nil;
    // Creating a fetch request to check whether the date already exists
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Date"];
    request.predicate = [NSPredicate predicateWithFormat:@"dateOfEvent = %@", enteredDate];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"dateOfEvent" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *dates = [context executeFetchRequest:request error:&error];
    if (!dates)
    {
        // Handle Error
    }
    else if (![dates count])
    {
        // If the dates count is 0 then let's create it
        date = [NSEntityDescription insertNewObjectForEntityForName:@"Date" inManagedObjectContext:context];
        date.dateOfEvent = enteredDate;
    }
    else
    {
        // If the object exists, just return the last object .
        date = [dates lastObject];
    }
    return date;
}

The dateOfEvent attribute is of type NSDate. I am using the method below to ensure the section titles in the first table view are formatted correctly:

-(NSString *)sectionDateFormatter
{
    return [NSDateFormatter localizedStringFromDate:self.dates.dateOfEvent
                                          dateStyle:NSDateFormatterLongStyle
                                          timeStyle:NSDateFormatterNoStyle];
}

My question is: how do I use a NSPredicate in the DateTableView to say, only show me the year information and then use cellForRow to create ONE cell for each year.

There is going to have to be two parts to this; one I need to ensure the Date Table view does not have any duplicate entries, like multiple cells for 2013, AND to ensure the DateTable View only shows me the years, etc.

I suspect I may have to use NSDateComponents like this question Core Data Predicate Date Comparison but I'm really sorry, I'm a newbie and I honestly have no idea how to tackle this.

My Date NSFetchedReusltsController is like this:

- (NSFetchedResultsController *)fetchedResultsController
{
    NSManagedObjectContext *managedObjectContext = [self managedObjectContext];

    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Date" inManagedObjectContext:managedObjectContext];
    fetchRequest.entity = entity;

// EDIT: Code Predicate added from Answer

NSDate *theDate = self.date.dateOfEvent;

NSCalendar *currentCalendar = [NSCalendar currentCalendar];
NSDateComponents *components = [currentCalendar components:NSYearCalendarUnit fromDate:theDate];

NSInteger year = components.year;


NSDate *startDate = [currentCalendar dateFromComponents:components];
components.year++;

NSDate *endDate = [currentCalendar dateFromComponents:components];

fetchRequest.predicate = [NSPredicate predicateWithFormat:@"dateOfEvent >= %@ AND dateOfEvent < %@", startDate, endDate];


    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"dateOfEvent" ascending:NO];
    fetchRequest.sortDescriptors = [NSArray arrayWithObject:sort];
    fetchRequest.fetchBatchSize = 20;
    NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil];
    self.fetchedResultsController = theFetchedResultsController;
    _fetchedResultsController.delegate = self;
    return _fetchedResultsController;
}

Any assistance would be seriously appreciated - I just have no thoughts on how to achieve this!

Upvotes: 0

Views: 295

Answers (1)

graver
graver

Reputation: 15213

NSCalendar *currentCalendar = [NSCalendar currentCalendar];
NSDateComponents *components = [currentCalendar components:NSYearCalendarUnit fromDate:[NSDate date]];
components.year = 2012;

NSDate *startDate = [currentCalendar dateFromComponents:components];
components.year++;

NSDate *endDate = [currentCalendar dateFromComponents:components];

fetchRequest.predicate = [NSPredicate predicateWithFormat:@"dateOfEvent >= %@ AND dateOfEvent < %@", startDate, endDate];

Upvotes: 2

Related Questions