0

Given an NSManagedObject subclass with a boolean property deleted (this is demonstrated in two different ways with code below since both approaches are not working):

[Code Listing 1]

@interface MyManagedObject : NSManagedObject

@property (nonatomic, retain) NSNumber *deleted;
// Or @property (nonatomic) BOOL deleted;

@end

created and inserted into Core Data as follows:

[Code Listing 2]

metadata.deleted = [NSNumber numberWithBool:NO];
// metadata.deleted = NO;

and fetched

[Code Listing 3]

// setup entity description
NSEntityDescription* entityDescription = [self entityDescription];

// setup the sorter
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"createdAt" ascending:YES];
NSSortDescriptor* sortDescriptorSection = [[NSSortDescriptor alloc] initWithKey:@"myManagedObject.category.title" ascending:YES];

// Build request
NSFetchRequest* request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
[request setSortDescriptors:[NSArray arrayWithObjects:sortDescriptorSection, sortDescriptor, nil]];
[request setPredicate:[NSPredicate predicateWithFormat:@"deleted == %@", [NSNumber numberWithBool:NO]]];

// Fetch request
NSArray* items = [[self managedObjectContext] executeFetchRequest:request error:nil];

returns one item in the items array as expected. The problem is when deleted is modified:

[Code Listing 4]

MyManagedObject* myManagedObject; // Assume initialized
myManagedObject.deleted = [NSNumber numberWithBool:YES];
// myManagedObject.deleted = YES;

// Printing description of myManagedObject in debugger shows deleted = 0 at this point

[myManagedObject.managedObjectContext save:nil];

// Printing description of myManagedObject in debugger still shows deleted = 0 at this point

BOOL testValue = myManagedObject.deleted;
if (testValue) {
    NSLog(@"value updated"); // This line is executed
}

Re-executing code listing 3 still yields one item in the items array even after a NSFetchResultsController watching the database has fired an update. If the application is terminated and relaunched, re-executing code listing 3 yields no items in the items NSArray.

Joshcodes
  • 8,513
  • 5
  • 40
  • 47
  • Your predicate references "deleted" but your attributes are named "deleted1" and "deleted2". Based on the fact that it behaves differently after relaunching, I assume this is a typo. – sbaker Aug 07 '13 at 15:29
  • 1
    Are "deleted", "deleted1", "deleted2" different properties? Then how can you expect that modifying "deleted1" or "deleted2" has any effect on "deleted"? - Or is "deleted1/deleted2" just a placeholder for the various methods to work with "deleted"? Then it is a duplicate of http://stackoverflow.com/questions/16283252/nsfetchedresultscontroller-updating-with-wrong-update-type: **Thou shalt not call a Core Data property "deleted"**. – Martin R Aug 07 '13 at 15:30
  • deleted1 and deleted2 were my way of avoiding putting together two sets of code listings demonstrating the property as both an NSNumber and a BOOL. Obviously, I was unaware that naming an variable deleted has any relevance to how modifications take effect. – Joshcodes Aug 07 '13 at 15:49
  • BTW: This is not a duplicate because, while the solution is the same, the symptoms are different (value change not taking effect as opposed to wrong update method being called). Therefore, the other question will not be found in a search by someone with this problem. @MartinR, feel free to post you comment as an answer and I will gladly accept. – Joshcodes Aug 07 '13 at 15:53
  • When using a BOOL in a predicate just do @"!deleted" or @"deleted" not @"deleted == something". – Fogmeister Aug 07 '13 at 16:33

3 Answers3

2

Calling a Core Data property "deleted" conflicts with the isDeleted property of NSManagedObject.

Compare Core Data NSPredicate "deleted == NO" does not work as expected for a similar problem and some experiments.

Btw. calling a property "updated" causes also problems, compare Cannot use a predicate that compares dates in Magical Record.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • is there any document from apple supporting this? – Keale Jul 30 '14 at 01:10
  • @Keale: There are some remarks in the Overview section of the [NSPropertyDescription Class Reference](https://developer.apple.com/library/mac/documentation/cocoa/reference/CoreDataFramework/Classes/NSPropertyDescription_Class/NSPropertyDescription.html). Unfortunately, these are pretty vague (*"You should avoid very general words ..."*) – Martin R Jul 30 '14 at 16:49
  • wow that's very vague indeed. I think a good way to avoid this is adding your unique special symbol before or after the property to guarantee uniqueness. anyway thanks for the response. – Keale Jul 31 '14 at 01:26
0

You should not be using deleted as a property name for an NSManagedObject subclass.

Also, deleted is an NSNumber, not a BOOL. So, when you are using:

BOOL testValue = myManagedObject.deleted;
if (testValue) {
    NSLog(@"value updated"); // This line is executed
}

You are testing if myManagedObject's deleted property is nil or not. If there is a value (even [NSNumber numberWithBool:YES]), testValue will be true.

On an unrelated note, I also advice to capture and log the error when calling NSManagedObjectContext's save method.

J2theC
  • 4,412
  • 1
  • 12
  • 14
0

Note that this isn't just a problem with deleted and isDeleted. I wrote a database in which I had a "relationship" property, which was an int16 (choice of several relationship types), but then I also had an "isRelationship" boolean which looked at this property and several others to determine whether the content was about the individual or about a relationship. valueForKey:@"relationship" would return the bool for isRelationship - which meant that it also affected NSPredicate. The following predicate:

NSPredicate *pred = [NSPredicate predicateWithFormat:@"relationship IN %@",
    @[[NSNumber numberWithShort:Relationship_Dating],
      [NSNumber numberWithShort:Relationship_Married]]];

would fail to filter out friend or family relationships because they were all returning 1 from isRelationship instead of the actual relationship value.

Watch out for stupidly "clever" system behavior when "is" booleans are involved.

(I fixed this by changing the "-(bool) isRelationship" method to "-(bool) isRelational".)

Apollo Grace
  • 361
  • 3
  • 16