How to delete all objects through a core data relationship?

27,591

Solution 1

If you want to delete the employee elements for a specific department, then you could run a for-in loop like for

for (Employees * theEmployee in department.employees) {
  [self.managedObjectContext deleteObject:[self.managedObjectContext objectWithID:theEmployee.objectID]]; 
}

Then save your managed context. IF of course that's what you want, and not remove the relationship between employees and departement; in that case, assigning an empty set would work.

Variation on above:

for (Employee *employeeToDelete in department.employees) {
    [self.managedObjectContext deleteObject:employeeToDelete];
}

Solution 2

Setting the department's employee relationship to an empty set WILL NOT delete the employees, regardless of the deletion rule. I believe you are misunderstanding the deletion rule. According the apple docs: "A relationship's delete rule specifies what should happen if an attempt is made to delete the source object". Thus, cascading will only take effect if we DELETE the department. By setting the relationship to an empty set, all we are doing is separating the employees from the department, not deleting them. And if that relationship is not set to optional for the employees, this will cause an error when saving. If you want to delete the employees from the department, you can iterate through them as listed above, or set the department relationship to cascade and then delete the department.

Solution 3

I also had something like below but it didn't work...

- (BOOL)deleteCollection:(Collection *)collection {
    // Grab the context
    NSManagedObjectContext *context = [self managedObjectContext];
    NSError *error = nil;

    [collection removeSounds:collection.sounds];
    [context deleteObject:collection];

    // Save everything
    if ([context save:&error]) {
        return YES;
    }
    return NO;
}

Obviously the database layer can't delete the sounds and then the collection at once. Setting the delete rule on the relationship to 'cascade' nicely solved my problem and let me use just:

[context deleteObject:collection];

If you want to save some people detailed reading time then just mark this as the answer.

As Fervus stated above, this link may also be helpful for the pros: Propagate deletes immediately in Core Data

Share:
27,591
stifin
Author by

stifin

I've been in the IT industry now for over 20 years. Many years ago, was a COBOL, VB5/6, SQL-Server, Cocoa/Obj-C, C# developer, and tinkered with Python and JavaScript, but would be embarrassed trying to answer most technical questions on these nowadays. However, while managing software teams and companies for the last 15+ years, I do occasionally do some coding either out of necessity or interest. I've been using Power Automate for a year now to make huge productivity gains in project administration.

Updated on January 07, 2020

Comments

  • stifin
    stifin over 4 years

    Say I have a custom NSManagedObject Department and this has a property representing a to-many relationship to employees i.e. NSSet *employees;.

    For a given Department, I want to remove all objects in employees. What is the recommended/best way to do this, please?

    So, hypothetically, my code would look like this:

    Department.h

    @interface Department: NSManagedObject {
    }
    @property (retain) NSString *departmentName;
    @property (retain) NSSet *employees;
    @end
    

    Department.m

    @implementation Department
    @dynamic departmentName;
    @dynamic employees;
    

    Employee.h

    @interface Employee: NSManagedObject {
    }
    @property (retain) NSString *firstName;
    @property (retain) NSString *lastName;
    @property (retain) Department *worksIn;
    @end
    

    doCoreDataStuff

    - (void)doCoreDataStuff:sender {
        //add a department, give it a couple employees, then try to remove those employees
        NSEntityDescription *deptEntity = [NSEntityDescription entityForName:@"Department"
                                                     inManagedObjectContext:self.managedObjectContext];
        Department *dept = [Department alloc] initWithEntity:deptEntity
                              insertIntoManagedObjectContext:self.managedObjectContext];
        NSError *error;
    
        dept.departmentName = @"Accounting";
        //save more often than normal to see more easily what causes error
        if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]);
    
        NSEntityDescription *empEntity = [NSEntityDescription entityForName:@"Employee"
                                                     inManagedObjectContext:self.managedObjectContext];
        emp.firstName = @"Steve";
        emp.lastName = @"Smith";
        emp.worksIn = dept;
    
        if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]);
    
        emp = [[Employee alloc] initWithEntity:empEntity
                insertIntoManagedObjectContext:self.managedObjectContext];
        emp.firstName = @"Natasha";
        emp.lastName = @"Johnson";
        emp.worksIn = dept;
    
        if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]);
    
        //all good so far! now will try to delete all employees for this department
        dept.employees = [NSSet set];
        if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); //"Multiple validation errors occurred."
    
        //this also produces the same error
        [[dept mutableSetValueForKey:@"employees"] removeAllObjects];
        if (![self.managedObjectContext save:&error]) NSLog(@"\nError: %@", [error localizedDescription]); //"Multiple validation errors occurred."
    

    The relationship employees is not optional so I'm guessing that removing employees from the department means that I am trying to "orphan" the employees i.e. keep employees in the persisted model without an associated department.

    So, I think my original question should be reworded to: what is best/recommended way to remove all "child" objects of a "parent" when the children have a non-optional relationship with the parent?

    I suspect that the answer is going to be "loop through and delete the employee objects one at a time".

    UPDATE

    According to an answer and a link to Apple's documentation, I should be able to set the delete rule to "Cascade" and then code like department.employees = [NSSet set]; will work. However, this does not work in my very simple project where I have set the delete rule accordingly.

    Thanks