Creating Core Data Entity working but fetching an Entity doesn't work - ios

So, I have a `NSManagedObject's User, Boundary, and Preset. A Preset is always tied to a single user. A user can have many Presets. Each Preset may be tied a boundary.
Basicly, the User has a Preset that he can save to each Boundary he has.
User has a to-many relationship to Preset.
Boundary has a many-to-many relationship to Preset.
I am trying to generate a list of Presets that the User has minus the ones already tied to the boundary.
I am using Magical Records.
My issue is when I create a new User and Boundary, this works:
Boundary *boundary = [Boundary MR_createEntity];
boundary.name = #"test boundary";
UserDB *user = [UserDB MR_createEntity];
user.username = #"test User";
Preset *preset01 = [Preset MR_findFirstByAttribute:#"nameDisplay" withValue:#"C4"];
DLog(#"preset01.nameDisplay: %#", preset01.nameDisplay);
Preset *preset02 = [Preset MR_findFirstByAttribute:#"nameDisplay" withValue:#"B"];
DLog(#"preset02.nameDisplay: %#", preset02.nameDisplay);
[boundary setPresets:[NSSet setWithObject:preset01]];
[user setPresets:[NSSet setWithObjects:preset01, preset02, nil]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user == %# AND boundary != %#", user, boundary];
NSArray *presetsList = [Preset MR_findAllWithPredicate:predicate];
DLog(#"presetsList: %#", presetsList);
So I set preset01 to the boundary. I set preset01 and preset02 to the user. So I need a list that shows preset02 to the user (since preset01 is already tied to the boundary, the user shouldn't be able to add it again).
DEBUG | -[LoginViewController viewDidLoad] | preset01.nameDisplay: C4
DEBUG | -[LoginViewController viewDidLoad] | preset02.nameDisplay: B
DEBUG | -[LoginViewController viewDidLoad] | presetList: (
"<SoilTestPointPreset: 0x1e06b8b0> (entity: Preset; id: 0x1e06bca0 <x-coredata://7476DB86-AF79-445C-B3AE-6C91088704A0/Preset/p98> ; data: {\n attributes = \"<relationship fault: 0x1e074000 'attributes'>\";\n boundary = nil;\n gpsLocation = nil;\n nameDisplay = B;\n nameTitle = nil;\n rgbColor = \"0x1e06b1d0 <x-coredata://7476DB86-AF79-445C-B3AE-6C91088704A0/RGBColor/p49>\";\n testing = nil;\n user = \"0x1e05ff60 <x-coredata:///UserDB/t59757D99-2FBF-4FBF-97AF-39582FC4B5503>\";\n})"
)
Thats what I expected. But now when I fetch the User and Boundary objects:
Boundary *boundary = [Boundary MR_findFirstByAttribute:#"boundaryID" withValue:#3748];
DLog(#"boundary.name: %#", boundary.name);
UserDB *user = [UserDB MR_findFirstByAttribute:#"uid" withValue:#99];
DLog(#"user.username: %#", user.username);
My array is empty:
DEBUG | -[LoginViewController viewDidLoad] | boundary.name: 997677
DEBUG | -[LoginViewController viewDidLoad] | user.username: thatPerson
DEBUG | -[LoginViewController viewDidLoad] | preset01.nameDisplay: C4
DEBUG | -[LoginViewController viewDidLoad] | preset02.nameDisplay: B
DEBUG | -[LoginViewController viewDidLoad] | presetList: (
)
Why does fetching the User and Boundary from Core Data change the results as opposed to creating them?
UPDATE:
I added:
Boundary *boundary = [Boundary MR_findFirstByAttribute:#"boundaryID" withValue:#3748];
DLog(#"boundary.name: %#", boundary.name);
DLog(#"boundary.presets.count: %d", boundary.presets.count); // Added
UserDB *user = [UserDB MR_findFirstByAttribute:AVI_UID withValue:#99];
DLog(#"user.username: %#", user.username);
DLog(#"user.presets.count: %d", user.presets.count); // Added
DLog(#"AFTER | boundary.presets.count: %d", boundary.presets.count); //Added
DLog(#"AFTER | user.presets.count: %d", user.presets.count); //Added
[[NSManagedObjectContext MR_contextForCurrentThread] MR_saveToPersistentStoreAndWait]; //Added
They User and Boundary have Presets relationships:
DEBUG | -[LoginViewController viewDidLoad] | boundary.name: 997677
DEBUG | -[LoginViewController viewDidLoad] | BEFORE | boundary.presets.count: 0
DEBUG | -[LoginViewController viewDidLoad] | user.username: iDealer
DEBUG | -[LoginViewController viewDidLoad] | BEFORE | user.presets.count: 0
DEBUG | -[LoginViewController viewDidLoad] | preset01.nameDisplay: C4
DEBUG | -[LoginViewController viewDidLoad] | preset02.nameDisplay: B
DEBUG | -[LoginViewController viewDidLoad] | AFTER | boundary.presets.count: 1
DEBUG | -[LoginViewController viewDidLoad] | AFTER | user.presets.count: 2
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x20831a80) → Saving <NSManagedObjectContext (0x20831a80): *** DEFAULT ***> on *** MAIN THREAD ***
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x20831a80) → Save Parents? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x20831a80) → Save Synchronously? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x1f59e860) → Saving <NSManagedObjectContext (0x1f59e860): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x1f59e860) → Save Parents? 0
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x1f59e860) → Save Synchronously? 1
__70-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:]_block_invoke21(0x1f59e860) → Finished saving: <NSManagedObjectContext (0x1f59e860): *** BACKGROUND SAVING (ROOT) ***> on *** MAIN THREAD ***
DEBUG | -[LoginViewController viewDidLoad] | presetList: (
)

You need to save your data before you can fetch it from a store. Use MR_saveToPersistentStoreAndWait or other variants. Fetching always goes it 'disk', so saving first should fix this.

After a bunch of playing around, I narrowed down the issue to the NSPredicate I was using:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user == %# AND boundary != %#", user, boundary];
There seems to be an issue with NOT and NONE in the queries. Got the answer in a different question I post: NSPredicate with a !=?
The answer involves using a SUBQUERY instead of NOT.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"user == %# AND SUBQUERY(boundary, $p, $p == %#).#count == 0", user, boundary];
I'm guessing the fetches I was making were fine, it would return 0 because of the bad NSPredicate, but I have not positive.

Related

UITableView crashes because "Attempt to create two animations for cell" (iOS 7)

In an UITableView I am trying to exchange the position of two sections and to add a new row into one of them by using a batch update.
BEFORE:
+-------------------------------------+
| |
| |
| SECTION A - HEADER - TITLE |
|-------------------------------------|
| SECTION A - ROW X |
|-------------------------------------|
| SECTION A - ROW Y > |
|-------------------------------------|
| |
| |
|-------------------------------------|
| SECTION B - ROW X |
|-------------------------------------|
| |
| |
+-------------------------------------+
AFTER:
+-------------------------------------+
| |
| |
|-------------------------------------|
| SECTION B - ROW X |
|-------------------------------------|
| |
| |
| SECTION A - HEADER - TITLE |
|-------------------------------------|
| SECTION A - ROW X |
|-------------------------------------|
| SECTION A - ROW Z - NEW ROW | <---------
|-------------------------------------|
| SECTION A - ROW Y > |
|-------------------------------------|
| |
| |
+-------------------------------------+
Here is my code:
[self.tableView beginUpdates];
[self.tableView moveSection:from toSection:to]; // from: 0, to: 1
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; // indexPath.section: 1, indexPath.row: 1
[self.tableView endUpdates];
But the UITableView instance crashes and outputs the following:
2014-06-29 19:45:07.486 YouTube[3312:60b] *** Assertion failure in -[_UITableViewUpdateSupport _setupAnimationsForNewlyInsertedCells], /SourceCache/UIKit_Sim/UIKit-2935.137/UITableViewSupport.m:1173
2014-06-29 19:45:07.488 YouTube[3312:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempt to create two animations for cell'
*** First throw call stack:
(
0 CoreFoundation 0x0000000102447495 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x00000001021a699e objc_exception_throw + 43
2 CoreFoundation 0x000000010244731a +[NSException raise:format:arguments:] + 106
3 Foundation 0x0000000101d42f19 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 189
4 UIKit 0x000000010101166c -[_UITableViewUpdateSupport(Private) _setupAnimationsForNewlyInsertedCells] + 7491
5 UIKit 0x000000010101aa81 -[_UITableViewUpdateSupport _setupAnimations] + 193
6 UIKit 0x0000000100e10615 -[UITableView _updateWithItems:updateSupport:] + 1639
7 UIKit 0x0000000100e0c000 -[UITableView _endCellAnimationsWithContext:] + 11615
...
Is it not possible to move a section and add a new row at the same time in a batch update?
I know this is an old question, but just ran into this myself on iOS 11.3. We have code that diffs old and new models and generates UITableView updates, and it explodes with the "more than one animation for cell" exception when a section is both moved and has any changes to its cells (insert/delete/update).
The only solution is to delete/insert the section in this case, rather than move. You could either always do this (and probably animate sub-optimally), or do what we do and track when cells change and do the move only when safe.
You should change dataSources as well.
- (void)exchangeTableSection
{
[self.tableView beginUpdates];
NSMutableArray *temp = self.data1;
self.data1 = self.data2;
self.data2 = temp;
[self.data1 insertObject:#"ROW Z - NEW ROW " atIndex:1];
[self.tableView moveSection:0 toSection:1];
[self.tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:1 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (0 == section) {
return self.data1.count;
} else if (1 == section) {
return self.data2.count;
}
return 0;
}

Irregular retain counts of NSNumbers

I am using NSNumbers throughout my app (non-ARC) using different syntaxes. Just to be a little more informed, I tried to see how NSNumbers are retained depending on their initialization syntaxes. So I did the following:
NSNumber* a = #1;
NSNumber* b = [[NSNumber alloc] initWithInt:2];
NSNumber* c = [NSNumber numberWithInt:3];
NSLog(#"%d | %d | %d", a.retainCount, b.retainCount, c.retainCount);
This code fragment is executed with a button tap, and the output has perplexed me (repetitive taps) :
73 | 27 | 6
78 | 159 | 22
78 | 160 | 22
78 | 161 | 22
78 | 162 | 22
78 | 163 | 22
85 | 169 | 22
85 | 170 | 22
85 | 171 | 22
85 | 172 | 22
Now this does not really have a purpose (at least not in my case), but I would like to know how these NSNumbers get to these retain counts.
You should never use retainCount. NEVER. look here
In Objective-C, retainCount is the number which controls the lifespan of an object. The object remains alive until the retainCount turns 0, and then the object gets deallocated. This is the big picture, with many exceptions, but this is the rule that applies here.
Those retain counts mean that those numbers are used somewhere in your application. Some other objects have retained them. Since your own code does not, this means that some other system objects do.
We'll profile your application with the "Allocation" instrument, and see what it can tell us. Here is the code we'll run:
NSNumber* a = #1;
NSNumber* b = [[[NSNumber alloc] initWithInt:2] autorelease];
NSNumber* c = [NSNumber numberWithInt:3];
NSLog(#"%d | %d | %d", a.retainCount, b.retainCount, c.retainCount);
[[[UIAlertView alloc] initWithTitle:#"number b"
message:[NSString stringWithFormat:#"address: %p, retainCount: %d", b, b.retainCount] delegate:nil
cancelButtonTitle:nil
otherButtonTitles:nil] show];
This alert will tell us what is the address of the number. Instrument will let us track this object's life.
Let's choose the Debug configuration in the profile setup of our scheme. Let's check the "Record reference count" in the "Allocations" instrument options. And see what we can get.
See? This number is indeed used by many system frameworks. Now you know why it has such a big retain count :-)

CoreData could not fulfill a fault with unmodified relationship

I have a entity A, it has a relationship B.
Here is a array fetched from CoreData:
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"isSynchronized = NO"];
NSManagedObjectContext *context = [NSManagedObjectContext MR_contextForCurrentThread];
NSArray * newWordsToSynchronize = [A MR_findAllWithPredicate:predicate inContext:context];
NSMutableArray * newWordsParameters = [NSMutableArray arrayWithCapacity:[asToSynchronize count]];
[newWordsToSynchronize enumerateObjectsUsingBlock:^(A * a, NSUInteger idx, BOOL * stop) {
NSLog(#"============= may have some problem ============");
NSLog(#"[a hasFaultForRelationshipNamed:#\"b\"] = %d", [a hasFaultForRelationshipNamed:#"b"]);
NSLog(#"a.word = %#", a.word);
NSLog(#"a.b.objectID = %#", a.b.objectID);
NSLog(#"a.managedObjectContext = %#", a.managedObjectContext);
NSLog(#"a.b.managedObjectContext = %#", [a.b managedObjectContext]);
NSLog(#"a.b.managedObjectContext = %#", [a.b managedObjectContext]);
NSLog(#"a.managedObjectContext = %#", a.managedObjectContext);
NSDictionary *paramDictionary = #{
#"nbook" : a.b.name,
#"lang" : a.lang,
...
};
[newWordsParameters addObject:paramDictionary];
}];
The [a.b managedObjectContext] method may return nil when I call it at second time and then the app goes crash.
Crash log from console:
2014-01-20 14:29:14.561 MyApp[10563:2e07] a.b.objectID = 0x1754e940 <x-coredata://DFC4F956-D5AC-4264-8717-C07090CA9547/B/p1>
2014-01-20 14:29:14.561 MyApp[10563:2e07] a.managedObjectContext = <NSManagedObjectContext: 0x1754bc00>
2014-01-20 14:29:14.561 MyApp[10563:2e07] a.b.managedObjectContext = <NSManagedObjectContext: 0x1754bc00>
2014-01-20 14:29:14.562 MyApp[10563:2e07] a.b.managedObjectContext = (null)
*** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x1754e940 <x-coredata://DFC4F956-D5AC-4264-8717-C07090CA9547/B/p1>''
*** First throw call stack:
(
0 CoreFoundation 0x037495e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x034cc8b6 objc_exception_throw + 44
2 CoreData 0x031a233b _PFFaultHandlerLookupRow + 2715
3 CoreData 0x031a1897 -[NSFaultHandler fulfillFault:withContext:forIndex:] + 39
4 CoreData 0x031a1473 _PF_FulfillDeferredFault + 259
5 CoreData 0x031a12c6 _sharedIMPL_pvfk_core + 70
6 CoreData 0x031e7130 _pvfk_5 + 32
7 MyApp 0x00181b00 __68-[CichangAHTTPEngine synchronizeAsOnCompletion:failure:]_block_invoke + 1760
8 CoreFoundation 0x037435eb __NSArrayEnumerate + 571
9 CoreFoundation 0x03743196 -[NSArray enumerateObjectsWithOptions:usingBlock:] + 102
10 CoreFoundation 0x037430a5 -[NSArray enumerateObjectsUsingBlock:] + 53
11 MyApp 0x0018105b -[CichangAHTTPEngine synchronizeAsOnCompletion:failure:] + 1179
12 MyApp 0x00185d9e __65-[CichangAHTTPEngine downloader:didFinishedDownloadObject:]_block_invoke_3 + 2030
13 libdispatch.dylib 0x040717f8 _dispatch_call_block_and_release + 15
14 libdispatch.dylib 0x040864b0 _dispatch_client_callout + 14
15 libdispatch.dylib 0x04074eeb _dispatch_root_queue_drain + 287
16 libdispatch.dylib 0x04075137 _dispatch_worker_thread2 + 39
17 libsystem_pthread.dylib 0x04412dab _pthread_wqthread + 336
18 libsystem_pthread.dylib 0x04416cce start_wqthread + 30
)
libc++abi.dylib: terminating with uncaught exception of type _NSCoreDataException
I found that B will be deallocated after first [a.b managedObjectContext] calling.
I think it was mainly caused by Core Data 1550 error (If I do not call [a.b managedObjectContext], Core Data will reports "The operation couldn’t be completed. (Cocoa error 1550.)"). But what may cause 1550 error and how to fix it?
1550 error report:
Error Domain=NSCocoaErrorDomain Code=1550 "The operation couldn’t be completed. (Cocoa error 1550.)" UserInfo=0x167269c0 {NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1550.), Dangling reference to an invalid object.=null, NSValidationErrorObject=<A: 0x16bab420> (entity: A; id: 0x16baae50 <x-coredata://A85EE9AB-97C1-43C1-B92E-A6C906F0C1A8/A/p168> ; data: {
date = "2014-01-21 02:32:00 +0000";
expect = "2014-01-21 02:31:59 +0000";
isSynchronized = 1;
lang = en;
last = "2014-01-21 02:01:59 +0000";
level = 1;
b = "0x16bab460 <x-coredata://A85EE9AB-97C1-43C1-B92E-A6C906F0C1A8/B/p1>";
status = "-1";
studycount = 0;
trans = "v. \U6d4b\U91cf\Uff0c\U6743\U8861 ";
word = measure;
}), NSAffectedObjectsErrorKey=(
"<B: 0x16babd70> (entity: B; id: 0x16bab460 <x-coredata://A85EE9AB-97C1-43C1-B92E-A6C906F0C1A8/B/p1> ; data: <fault>)"
), NSValidationErrorKey=newWordBook, NSValidationErrorValue=<B: 0x16babd70> (entity: B; id: 0x16bab460 <x-coredata://A85EE9AB-97C1-43C1-B92E-A6C906F0C1A8/B/p1> ; data: <fault>), NSValidationErrorShouldAttemptRecoveryKey=true}
If the 1550 error happened, "CoreData could not fulfill a fault" crash will more easer to cause.
“Core Data could not fulfill fault” really means that there was a deletion. There was data in the persistent store. Then managed object representing this data was created. The object was a fault. Then the data in the store was deleted. Then some property of an object was accessed and Core Data tried to fulfill the fault, but couldn’t.
If you’re not doing explicit deletions, then also make sure that implicit deletions are not happening. Deletion behind the scenes could happen when one of your relationships was configured with the cascade deletion rule.
In your example this could happen if the relationship from A to B was configured with cascade rule, and object A was deleted.

CoreData: Inverse relationships causing crash on save

I have a bit of code in my application that generates a Core Data Model and populates it with a set of NSEntityDescriptions. Each of these entity descriptions then have an arbitrary number of NSPropertyDescriptions allocated for them. These properties are a combination of NSAttributeDescriptions and NSRelationshipDescriptions. All relationships are matched with an existing relationship and they are set as inverse of one another using setInverseRelationship:.
The attribute properties work fine, and to-many relationships work fine; I have tested that thoroughly. The issue seems to be with NSRelationshipDescriptions that have a maxCount value of 1, meaning the property description returns a isToMany value of NO. When the inverseRelationship property is set for these type of relationship, Core Data crashes when I try to save any object that utilizes that relationship with the error:
2013-11-09 11:17:15.068 Directory[1344:5c03] -[NSManagedObject count]: unrecognized selector sent to instance 0x9643820
2013-11-09 11:17:15.074 Directory[1344:5c03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSManagedObject count]: unrecognized selector sent to instance 0x9643820'
*** First throw call stack:
(
0 CoreFoundation 0x01f9f5e4 __exceptionPreprocess + 180
1 libobjc.A.dylib 0x01d228b6 objc_exception_throw + 44
2 CoreFoundation 0x0203c903 -[NSObject(NSObject) doesNotRecognizeSelector:] + 275
3 CoreFoundation 0x01f8f90b ___forwarding___ + 1019
4 CoreFoundation 0x01f8f4ee _CF_forwarding_prep_0 + 14
5 CoreData 0x0083c128 -[NSSQLCore _knownOrderKeyForObject:from:inverseToMany:] + 200
6 CoreData 0x00773080 -[NSSQLCore _populateRowForOp:withObject:] + 1120
7 CoreData 0x00789157 -[NSSQLCore recordValuesForInsertedObject:] + 71
8 CoreData 0x00771e8d -[NSSQLCore recordChangesInContext:] + 685
9 CoreData 0x00770c55 -[NSSQLCore saveChanges:] + 565
10 CoreData 0x0073d88c -[NSSQLCore executeRequest:withContext:error:] + 412
11 CoreData 0x0073d380 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 4704
12 CoreData 0x00769ffc -[NSManagedObjectContext save:] + 764
13 Directory 0x0002f2bf -[ETDatabaseController save] + 111
14 Directory 0x0002ba9c -[ETDataLoader performImportWithData:] + 10284
The insinuation I am gathering from this is it is considering the inverse relationship to be a to-many relationship when that is not the case. According to my assertions, what I know about each of my relationships is:
relationDescription.inverseRelationship != nil
[relationDescription.inverseRelationship.inverseRelationship isEqual:relationDescription]
I have tested this by creating the model and populating it with a small set of sample data. Currently, objects that have any sort of attributes, to-many relationships (with/without inverse), and to-one relationship (without inverse) work consistently. The issues comes when I try to have a to-one relationship with an inverse relationship.
This seems a bit convoluted so let me know if I need to clarify anything better. Thanks!
Edit 1:
The relationship creation is done in two steps, first it creates all the relationships, then it establishes the inverse of each relationship by using a lookup.
NSRelationshipDescription *description = [[NSRelationshipDescription alloc] init];
[description setName:self.name];
[description setDestinationEntity:entity];
[description setMaxCount:(isToMany ? 0 : 1)];
[description setMinCount:0];
[description setOrdered:YES]; // See you in a minute
later...
NSEntityDescription *inverseEntity = newRelationship.destinationEntity;
NSRelationshipDescription *inverseRelationDescription = [inverseEntity relationshipsByName][inverse.name];
if (inverseRelationDescription) {
inverseRelationDescription.inverseRelationship = newRelationship;
newRelationship.inverseRelationship = inverseRelationDescription;
} else if ([inverse.name isEqualToString:relation.name]) {
newRelationship.inverseRelationship = newRelationship;
}
Well, I guess writing Edit 1 made something click. So from what it looks like, if you call setOrdered: on a NSRelationDescription that is meant to be a to-one relationship, the internals of Core Data automatically considers it a to-many relationship, despite the conflict with the maxCount being one.
I fixed this by changing the creation code for the relationship from:
NSRelationshipDescription *description = [[NSRelationshipDescription alloc] init];
[description setName:self.name];
[description setDestinationEntity:entity];
[description setMaxCount:(isToMany ? 0 : 1)];
[description setMinCount:0];
[description setOrdered:YES];
to:
NSRelationshipDescription *description = [[NSRelationshipDescription alloc] init];
[description setName:self.name];
[description setDestinationEntity:entity];
[description setMaxCount:(isToMany ? 0 : 1)];
[description setMinCount:0];
if (isToMany)
[description setOrdered:YES];

How to use a predicate on NSTimeInterval?

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
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.
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])
Will add timeIntervalSince1970 with in a predicate itself. It woul
NSPredicate* predicate = PREDICATE(#"(lastUpdatedTime == nil OR lastUpdatedTime.timeIntervalSince1970 <= %f)",currentDate.timeIntervalSince1970);

Resources