Reputation: 482
My relation is shown on the picture below.
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
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
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