I would like to get all records for the current month, so I stack two predicates for first date of the month and last day of the month. Since I use CoreData the dates are stored actually as NSTimeInterval.
NSCalendar *calendar = [NSCalendar currentCalendar];
//Get beginning of current month
NSDateComponents *beginningOfCurrentMonthComponents = [calendar components:(NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:date];
[beginningOfCurrentMonthComponents setDay:1];
NSDate *beginningOfCurrentMonthDate = [calendar dateFromComponents:beginningOfCurrentMonthComponents];
//Set a single month to be added to the current month
NSDateComponents *oneMonth = [[NSDateComponents alloc] init];
[oneMonth setMonth:1];
//determine the last day of this month
NSDate *beginningOfNextMonthDate = [calendar dateByAddingComponents:oneMonth toDate:beginningOfCurrentMonthDate options:0];
NSMutableArray *parr = [NSMutableArray array];
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate >= %d", [beginningOfCurrentMonthDate timeIntervalSince1970]]];
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate < %d", [beginningOfNextMonthDate timeIntervalSince1970]]];
//Give me everything from beginning of this month until end of this month
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];
return [allRecords filteredArrayUsingPredicate:predicate];
Upon returning the filtered array, it crashes with this error message:
2013-10-25 19:09:39.702 [3556:a0b] -[__NSCFNumber timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x8911440
2013-10-25 19:09:39.704 [3556:a0b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x8911440'
*** First throw call stack:
(
0 CoreFoundation 0x01aa85e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x0182b8b6 objc_exception_throw + 44
2 CoreFoundation 0x01b45903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x01a9890b ___forwarding___ + 1019
4 CoreFoundation 0x01a984ee _CF_forwarding_prep_0 + 14
5 CoreFoundation 0x01a7e3e3 -[NSDate compare:] + 67
6 Foundation 0x014194fe -[NSComparisonPredicateOperator performPrimitiveOperationUsingObject:andObject:] + 408
7 Foundation 0x014b03de -[NSPredicateOperator performOperationUsingObject:andObject:] + 306
8 Foundation 0x014b016c -[NSComparisonPredicate evaluateWithObject:substitutionVariables:] + 347
9 Foundation 0x014299b6 -[NSCompoundPredicateOperator evaluatePredicates:withObject:substitutionVariables:] + 240
10 Foundation 0x01429845 -[NSCompoundPredicate evaluateWithObject:substitutionVariables:] + 294
11 Foundation 0x014b0009 -[NSPredicate evaluateWithObject:] + 48
12 Foundation 0x014aff89 _filterObjectsUsingPredicate + 418
13 Foundation 0x014afd42 -[NSArray(NSPredicateSupport) filteredArrayUsingPredicate:] + 328
I used this loop also to indicate that the recordDate truly exists within the array:
for (FTRecord *r in allRecords) {
NSLog(@"%f", [r recordDate]);
}
2013-10-25 19:09:35.860 [3556:a0b] 1380582000.000000
2013-10-25 19:09:36.556 [3556:a0b] 1380754800.000000
For solution in Swift 3:
let date = NSDate()
let calender = NSCalendar.current
var beginningOfCurrentMonthComponents = calender.dateComponents([.year, .month, .day], from: date as Date)
beginningOfCurrentMonthComponents.day = 1
let beginningOfCurrentMonthDate = calender.date(from: beginningOfCurrentMonthComponents)
let beginningOfNextMonth = calender.date(byAdding: .month, value: 1, to: beginningOfCurrentMonthDate!)
let pred1 = NSPredicate(format: "date >= %@", beginningOfCurrentMonthDate! as NSDate)
let pred2 = NSPredicate(format: "date < %@", beginningOfNextMonth! as NSDate)
let predicate = NSCompoundPredicate.init(andPredicateWithSubpredicates: [pred1, pred2])
You chose the "Use scalar properties for primitive data types" option when
creating the managed object subclass, so that the recordDate is represented as
NSTimeInterval
in the FTRecord
class.
But in a predicate like "recordDate >= 123.45"
, the left-hand side is stored
as a NSKeyPathExpression
, and that uses valueForKeyPath:@"recordDate"
to access the property, which returns an NSDate
object.
The right-hand side of that predicate is stored as NSConstantValueExpression
with a reference to an NSNumber
object.
Therefore an NSDate
is compared with an NSNumber
, which leads exactly to the
exception that you got.
To fix the problem, you have to compare the property with an NSDate
(which is what @rmaddy initially suggested):
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate >= %@", beginningOfCurrentMonthDate]];
[parr addObject:[NSPredicate predicateWithFormat:@"recordDate < %@", beginningOfNextMonthDate]];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:parr];
I tested this and it seems to produce the expected result.
Note however that Core Data uses timeIntervalSinceReferenceDate
and
dateWithTimeIntervalSinceReferenceDate
to convert between the scalar
values and NSDate
, not timeIntervalSince1970
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With