Search code examples
iossortingfilternsfetchrequestnsfetchedresultscontroller

Sort/Filter NSFetchRequest how to write sort descriptor taht will use current date?


I'm storing in Core Data 2 date properties, one is expire date and second is "amber" date (the date that is X weeks before expire date). I want to sort results in following way:

  • items before the amber date - (green) now < amber
  • items after amber date but before expire date - (amber) amber <= now < expire
  • items on or after expire date - (red) expire <= now

Can you help me with that?

UPDATE After more research I've found that I could fetch results unsorted, put it in the array and sort the array and table would use this array as data source - too much to change to have it working in my app. Also there is possibility to use extra parameter which will hold evaluated value based on the list above. The fetch then would require 2 steps:

  1. fetch data and run evaluation on each item
  2. fetch sorted data by this evaluated property

The latter solution is my best option, so could anyone help me with that part?

EDIT

The state property is defined as int16_t

@property (nonatomic, assign) int16_t state;

In viewDidLoad of the view with table I call fallowing before fetching the sorted results:

 NSFetchRequest *fetchRequest = 
[NSFetchRequest fetchRequestWithEntityName:@"MyTable"];
    NSSortDescriptor *sortTitle = 
    [NSSortDescriptor sortDescriptorWithKey:@"name"
                                  ascending:YES];
    [fetchRequest setSortDescriptors:@[sortByTitle]];

    NSFetchedResultsController *ctrl = [[NSFetchedResultsController alloc]
                initWithFetchRequest:fetchRequest
                managedObjectContext:[NSManagedObjectContext defaultContext]
                  sectionNameKeyPath:nil
                           cacheName:nil];

    NSError* fetchError = nil;
    if([ctrl performFetch:&fetchError] == NO) {

        NSLog(@"Error: perform fetch failed, %@",[fetchError description]);

    } else {
        //do we have results here?
        id <NSFetchedResultsSectionInfo> sectionInfo = 
                                      [ctrl.sections objectAtIndex:0];
        NSArray * items = [sectionInfo objects];
        for (int i=0; i<items.count; i++) {
            CourseCD* item = items[i];
            NSLog(@"%d - %@",i,item);
            [item evaluateState];//category adds this method
        }
    }

This is the method from category

-(State) evaluateState {
    CourseComplianceState pState = StateUnknown;
    if (self.expire == nil) {
        [self setState:pState];
        return pState;
    }

    NSDate* now = [[NSDate alloc] init];
    pState = StateNotExpired;

    if ([NSDate isDate:now inRangeFirstDate:self.thresholdDate 
              lastDate:self.expireDate]) {
        //amber
        pState = StateWithinThreshold;
    } else if ([NSDate isDate:now inRangeFirstDate:self.expireDate 
                     lastDate:[NSDate distantFuture]]){
        //expired
        pState = StateExpired;
    }

    //
    [self setState:pState];

    NSLog(@" - evaluateState -");

    return pState;
}

And this is the State struct

enum {
    StateUnknown,
    StateNotExpired,
    StateWithinThreshold,
    StateExpired

};
typedef int16_t State;

Then after this call I use the destination call with following sorts and I'm using controller that works with table.

//later I use following sort descriptors
NSSortDescriptor *sortState = [NSSortDescriptor 
                                  sortDescriptorWithKey:@"state"
                                              ascending:YES];
NSSortDescriptor *sortExpire = [NSSortDescriptor 
                                  sortDescriptorWithKey:@"expire"
                                              ascending:YES];

EDIT

NSSortDescriptor *sortByTitle = [NSSortDescriptor 
                                   sortDescriptorWithKey:@"name" 
                                               ascending:YES];
NSFetchRequest *fetchRequest = [NSFetchRequest 
                                 fetchRequestWithEntityName:@"Course"];
[fetchRequest setSortDescriptors:@[sortState,sortExpire,sortByTitle]];


self.resultsController = [[NSFetchedResultsController alloc] 
                           initWithFetchRequest:fetchRequest
                   managedObjectContext:[NSManagedObjectContext defaultContext]
                             sectionNameKeyPath:nil
                                      cacheName:nil];

self.resultsController.delegate = self;

NSError* fetchError = nil;
if([self.resultsController performFetch:&fetchError] == NO) {

    NSLog(@"Error: perform fetch failed, %@",[fetchError description]);

}

But it only sorts by the expire date and state is ignored or not saved by this first evaluation run.

EDIT 2014/06/10

This is the setter in the NSManagedObject class

//.h
@property (nonatomic, assign) int16_t state;

//.m
@synthesize state=_state;
-(void) setState:(int16_t)state {
    _state = state;

    NSError* error = nil;
    [[NSManagedObjectContext defaultContext] save:&error];
    if(error != nil){
        NSLog(@"Error in setState, details:%@",[error description]);
    }
}

FINAL EDIT - RESOLVED

The resolution was to: remove @synthesise state = _state; and -(void) setState:(int16_t)state {} and leave @dynamic state. Then in evaluateState instead of [self setState:pState]; I used self.state=pState; and now it sorts as required. The question now is why with synthesised property it didnt worked?


Solution

  • The resolution was to: remove @synthesise state = _state; and -(void) setState:(int16_t)state {} and leave @dynamic state. Then in evaluateState instead of [self setState:pState]; I used self.state=pState; and now it sorts as required.