Goal: I want to check a managed object to make sure that it is okay for deletion
Expectation: -[NSManagedObject validateForDelete:]
should return a BOOL
based on the delete rules setup in the managed object model
NSManagedObject Class Reference
validateForDelete:
Determines whether the receiver can be deleted in its current state.
- (BOOL)validateForDelete:(NSError **)error
Parameters
error
If the receiver cannot be deleted in its current state, upon return contains an instance of NSError that describes the problem.
Return Value
YES
if the receiver can be deleted in its current state, otherwiseNO
.Discussion
An object cannot be deleted if it has a relationship has a “deny” delete rule and that relationship has a destination object.
NSManagedObject
’s implementation sends the receiver’s entity description a message which performs basic checking based on the presence or absence of values.
Reading this I assume that I should be able to do something like this:
BOOL canDelete = [myManagedObject validateForDelete:&error];
if (canDelete) {
[managedObjectContext deleteObject:myManagedObject];
}
else {
[self requestUserFeedback];
}
However, this method almost always returns NO
.
Example Code
Managed Object Model
Department
- Attribute: name
- Value: String
- Relationship: employees
- To Many
- Destination: Employee
- Delete Rule: Nullify
- Inverse: department
Employee
- Attribute: name
- Value: String
- Relationship: department
- To One
- Destination: Department
- Delete Rule: Nullify
- Inverse: employees
Example Code:
NSManagedObject *department = [NSEntityDescription insertNewObjectForEntityForName:@"Department" inManagedObjectContext:self.managedObjectContext];
for (int i = 0; i < 10; i++) {
NSManagedObject *employee = [NSEntityDescription insertNewObjectForEntityForName:@"Employee" inManagedObjectContext:self.managedObjectContext];
[employee setValue:department forKey:@"department"];
if ([employee validateForDelete:NULL]) {
NSLog(@"Can delete employee");
}
else {
NSLog(@"WARNING: Cannot delete employee");
}
}
NSError *error;
BOOL canDelete = [department validateForDelete:&error];
if (canDelete) {
NSLog(@"Can delete department");
}
else {
NSLog(@"WARNING: Cannot delete department");
}
Output:
Can delete employee
Can delete employee
Can delete employee
Can delete employee
Can delete employee
Can delete employee
Can delete employee
Can delete employee
Can delete employee
Can delete employee
WARNING: Cannot delete department
To my surprise, canDelete
is NO
. Why would the department object be invalid for deletion when the employee objects are not invalid? The error returned contains the following (abbreviated)
Error Domain=NSCocoaErrorDomain
Code=1600
UserInfo= <>{
NSValidationErrorObject=< department object >
NSValidationErrorKey=employees
NSValidationErrorValue=Relationship 'employees' on managed object
}
So, validateForDelete:
is not returning what I expect for to-many relationships, but does return the expected value for to-one relationships. But, go back to the documentation for a second:
An object cannot be deleted if it has a relationship has [sic] a “deny” delete rule and that relationship has a destination object.
NSManagedObject’s implementation sends the receiver’s entity description a message which performs basic checking based on the presence or absence of values.
This calls out how the default implementation of validateForDelete: works and makes a concerted effort to draw attention to the "deny" delete rule. There is no mention of nullify or cascade delete rules or differences between to-one or to-many relationships.
Further Research
A Google search turns up this hit on the Apple Mailing List archives from 2009, which basically says: -[NSManagedObject validateForDelete:]
is a hook for subclassers, you need to implement your own logic in a separate method to determine if an object is valid for deletion.
What? Is this true? That's not what the documentation for this method would lead one to believe:
Determines whether the receiver can be deleted in its current state.
How are we suppose to check if a managed object is in a state that is okay for deletion?
I've set up a small sample project on GitHub that illustrates my findings with both production code and unit tests.