Jack Nutkins
Jack Nutkins

Reputation: 1555

Error: NSArray was mutated while being enumerated thrown when accessing globals and Core Data

I have this piece of code which I am using to update some values in Core Data when another object is added:

//Create new receipt
        Receipt *receipt = [[Receipt alloc] init];
        receipt.project = self.projectLabel.text;
        receipt.amount = self.amountTextField.text;
        receipt.descriptionNote = self.descriptionTextField.text;
        receipt.business = self.businessNameTextField.text;
        receipt.date = self.dateLabel.text;
        receipt.category = self.categoryLabel.text;
        receipt.paidBy = self.paidByLabel.text;
        receipt.receiptImage1 = self.receiptImage1;
        //Need to set this to 2
        receipt.receiptImage2 = self.receiptImage1;
        receipt.receiptNumber = @"99";

        int count = 0;
        int catCount = 0;

    for (Project *p in appDelegate.projects)
            {
                if ([p.projectName isEqualToString:receipt.project]){
                    double tempValue = [p.totalValue doubleValue];
                    tempValue += [receipt.amount doubleValue];
                    NSString *newTotalValue = [NSString stringWithFormat:@"%.02f", tempValue];
                    NSString *newProjectName = p.projectName;
                    //remove entity from Core Data
                    NSFetchRequest * allProjects = [[NSFetchRequest alloc] init];
                    [allProjects setEntity:[NSEntityDescription entityForName:@"Project" inManagedObjectContext:appDelegate.managedObjectContext]];
                    [allProjects setIncludesPropertyValues:NO]; //only fetch the managedObjectID

                    NSError * error = nil;
                    NSArray * projectsArray = [appDelegate.managedObjectContext executeFetchRequest:allProjects error:&error];

                    //Delete product from Core Data
                    [appDelegate.managedObjectContext deleteObject:[projectsArray objectAtIndex:count]];

                    NSError *saveError = nil;
                    [appDelegate.managedObjectContext save:&saveError];

                    [appDelegate.projects removeObjectAtIndex:count];

                    NSLog(@"Removed project from Core Data");

                    //Insert a new object of type ProductInfo into Core Data
                    NSManagedObject *projectInfo = [NSEntityDescription
                                                    insertNewObjectForEntityForName:@"Project" 
                                                    inManagedObjectContext:appDelegate.managedObjectContext];

                    //Set receipt entities values
                    [projectInfo setValue:newProjectName forKey:@"name"];
                    [projectInfo setValue:newTotalValue forKey:@"totalValue"];

                    if (![appDelegate.managedObjectContext save:&error]) {
                        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
                    }

                    NSLog(@"Added Project to Core Data");

                    Project *tempProject = [[Project alloc] init];
                    tempProject.projectName = [projectInfo valueForKey:@"name"];
                    tempProject.totalValue = [projectInfo valueForKey:@"totalValue"];

                    [appDelegate.projects addObject:tempProject];

                }
                count++;
            }

            for (Category *c in appDelegate.categories){

                if ([c.categoryName isEqualToString:receipt.category]){

                    double tempValue = [c.totalValue doubleValue];
                    tempValue += [receipt.amount doubleValue];
                    NSString *newTotalValue = [NSString stringWithFormat:@"%.02f", tempValue];
                    NSString *newCategoryName = c.categoryName;

                    //remove entity from Core Data
                    NSFetchRequest * allCategories = [[NSFetchRequest alloc] init];
                    [allCategories setEntity:[NSEntityDescription entityForName:@"Category" inManagedObjectContext:appDelegate.managedObjectContext]];
                    [allCategories setIncludesPropertyValues:NO]; //only fetch the managedObjectID

                    NSError * categoriesError = nil;
                    NSArray * categoriesArray = [appDelegate.managedObjectContext executeFetchRequest:allCategories error:&categoriesError];

                    //Delete product from Core Data
                    [appDelegate.managedObjectContext deleteObject:[categoriesArray objectAtIndex:catCount]];
                    NSError *categorySaveError = nil;
                    [appDelegate.managedObjectContext save:&categorySaveError];

                    [appDelegate.categories removeObjectAtIndex:catCount];

                    NSLog(@"Removed category from Core Data");
                    NSError * error = nil;
                    //Insert a new object of type ProductInfo into Core Data
                    NSManagedObject *categoryInfo = [NSEntityDescription
                                                     insertNewObjectForEntityForName:@"Category" 
                                                     inManagedObjectContext:appDelegate.managedObjectContext];

                    //Set receipt entities values
                    [categoryInfo setValue:newCategoryName forKey:@"name"];
                    [categoryInfo setValue:newTotalValue forKey:@"totalValue"];

                    if (![appDelegate.managedObjectContext save:&error]) {
                        NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
                    }

                    NSLog(@"Added Category to Core Data");

                    Category *tempCategory = [[Category alloc] init];
                    tempCategory.categoryName = [categoryInfo valueForKey:@"name"];
                    tempCategory.totalValue = [categoryInfo valueForKey:@"totalValue"];

                    [appDelegate.categories addObject:tempCategory];
                }
                catCount++;

            }

This code gives the error:

'...was mutated while being enumerated'.

Can anyone explain why? Also, is there a better approach to doing what I'm trying to achieve?

Upvotes: 1

Views: 2765

Answers (2)

occulus
occulus

Reputation: 17014

The error you're seeing is accurate. The problem is that you're mutating (changing) a collection while iterating over it. Basically, you're doing something of the form:

for (Project *p in appDelegate.projects) {
   ...
   [p addObject: X]
}

This isn't allowed.

One simple solution is to make a new collection of the objects you want to add, and then add them to the original container outside of your loop. Something like:

NSMutableArray *array = [NSMutableArray array];
for (Project *p in appDelegate.projects) {
   ...
   [array addObject:X];
}
[p addObjects:array];

By the way, did you google for the error text "was mutated while being enumerated"? I'd be surprised if you didn't find the answer for this common problem just by googling.

Also, when posting an error message, it's helpful to post the full line, not just part of it.

Upvotes: 5

MByD
MByD

Reputation: 137292

You are adding and removing items from appDelegate.projects while iterating over it in a for-each loop here:

[appDelegate.projects removeObjectAtIndex:count];
// ...
[appDelegate.projects addObject:tempProject];

And the same for appDelegate.categories:

[appDelegate.categories removeObjectAtIndex:count];
// ...
[appDelegate.categories addObject:tempProject];

AFAIK, in such case you should use a simple for loop, and access the array with index.

Upvotes: 0

Related Questions