apetrov
apetrov

Reputation: 482

CoreData Sort by relation

My relation is shown on the picture below.

CoreData relation

I'd like to sort Events first by those that are in active promotion and then by start date. Promotion is active if current date is between start and end date. Unfortunately, because of CoreData, I'm not able to use transient properties for sorting. In my controller I'm not using a fetch controller.

Is there any way to achieve that?

Update:

I've following sort descriptors:

// First is incorrect
[NSSortDescriptor(key: "promotion.start", ascending: false), 
NSSortDescriptor(key: "start", ascending: true)]

Predicates (They're ok, though):

let promotionsPredicate =
    NSPredicate(format: "(%@ >= promotion.start && %@ <= promotion.end) && " +
                "(ANY promotion.cities.id == %@)", NSDate(), NSDate(), objectID)
let eventsPredicate =
    NSPredicate(format: "start >= %@ && venue.city.id == %@",
                                NSDate(), objectID)

let subpredicates = [eventsPredicate, promotionsPredicate]                    
let compoundPredicate NSCompoundPredicate(orPredicateWithSubpredicates: subpredicates)

And this is the Request (I'm using CoreStore, but the idea should be clear):

class func pagedEventsForPredicateSortedByInPromoAndStartDate(predicate: NSPredicate,
                                                              descriptors: [NSSortDescriptor],
                                                              fetchOffset: Int,
                                                              fetchLimit: Int) -> [Event] {

    return CoreStore.fetchAll(From(Event),
                              Where(predicate),
                              OrderBy(descriptors),
                              Tweak { (fetchRequest) -> Void in
                                fetchRequest.fetchOffset = fetchOffset
                                fetchRequest.fetchLimit = fetchLimit
                                fetchRequest.returnsObjectsAsFaults = false
        }) ?? []
}

Upvotes: 0

Views: 1315

Answers (2)

kaushal
kaushal

Reputation: 1573

I will suggest you to create "isActive" transection property in Promotion Entity to calculate active record.

 NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
 NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
                                    initWithKey:@“start” ascending:YES];
 [request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]]; 
 [fetchRequest setIncludesPropertyValues:YES];
 [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];

After that you can filter fetch result by sorting :

NSPredicate *predicate = [NSPredicate predicateWithFormat:@“isActive == %@", @1];
        NSMutableArray *finalResult = [NSMutableArray arrayWithArray:[results filteredArrayUsingPredicate:predicate]];

HTH.

Upvotes: 1

Sander
Sander

Reputation: 1375

As I understood you have to get all Event objects, but just in proper order. To do that with such complicated order, that includes relationship, as far as I know you have to fetch all Events and then sort them using NSArray's method

- (NSArray<ObjectType> *)sortedArrayUsingComparator:(NSComparator)cmptr

Here are the pieces of the code

1. Fetch from Core Data

// get the right context here
NSManagedObjectContext *yourContext;

NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Event"];
// extra line, predicate is nil by default, any other required predicate could be written here
request.predicate = nil;

__block NSArray *results = nil;
[yourContext performBlockAndWait:^{

   NSError *error = nil;
   results = [yourContext executeFetchRequest:request error:&error];

       if (error) {
            // handle error here
        }
    }];

Fetch is made manually with core methods and you may use Magical Record or any other framework that works with Core Data to make it in a row.

2. Sort the results

__weak typeof(self) weakSelf = self;

NSDate *now = [NSDate date];
NSArray *sortedResults = [results sortedArrayUsingComparator:^NSComparisonResult(Event *_Nonnull obj1, Event *_Nonnull obj2) {

    BOOL isObj1InActivePromotion = [weakSelf date:now isBetweenDate:obj1.promotion.start andDate:obj1.promotion.end];
    BOOL isObj2InActivePromotion = [weakSelf date:now isBetweenDate:obj2.promotion.start andDate:obj2.promotion.end];

    // if they eather are in active promotion or no, just compare them by start date of the Event
    if (isObj1InActivePromotion == isObj2InActivePromotion) {
        return [obj1.start compare:obj2.start];
    } else {
        return isObj1InActivePromotion ? NSOrderedAscending : NSOrderedDescending;
    }
}];

3. Additional method to work with NSDate

This method was used in sorting method

+ (BOOL)date:(NSDate *)date isBetweenDate:(NSDate *)beginDate andDate:(NSDate *)endDate
{
    if ([date compare:beginDate] == NSOrderedAscending) {
        return NO;
    }

    if ([date compare:endDate] == NSOrderedDescending) {
        return NO;
    }

    return YES;
}

I could't check the code for obvious reasons, so sorry for any typos if they are.

Upvotes: 1

Related Questions