170
votes

I am getting a strange error message from the core data when trying to save but the problem that the error is not reproducible ( it appears at different times when doing different tasks)

the error message:

Unresolved error Domain=NSCocoaErrorDomain Code=1560 UserInfo=0x14f5480 "Operation could not be completed. (Cocoa error 1560.)", {
NSDetailedErrors = (
Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x5406d70 "Operation could not be completed. (Cocoa error 1570.)",
Error Domain=NSCocoaErrorDomain Code=1570 UserInfo=0x14f9be0 "Operation could not be completed. (Cocoa error 1570.)"
);
}

and the method that generates the error is:

- (IBAction)saveAction:(id)sender {
    NSError *error;
    if (![[self managedObjectContext] save:&error]) {
        // Handle error
        NSLog(@"Unresolved error %@, %@, %@", error, [error userInfo],[error localizedDescription]);
        exit(-1);  // Fail
    }
}

any idea for the reason of this message ? giving that it appears at random times

7
This might help you: "iPhone Core Data “Production” Error Handling" stackoverflow.com/questions/2262704/…Johannes Fahrenkrug

7 Answers

297
votes

It means there's a mandatory property has been assigned nil. Either in your *.xcodatamodel check the "optional" box or when you are saving to the managedObjectContext make sure that your properties are filled in.

If you're getting further errors after changing your code to suit the two requirements try cleaning your build and delete the application from your iPhone Simulator/iPhone device. Your model change may conflict with the old model implementation.

Edit:

I almost forgot here's all the error codes that Core Data spits out: Core Data Constants Reference I had trouble with this before and I realised I unchecked the correct optional box. Such trouble finding out the problem. Good luck.

235
votes

I struggled with this for a little while myself. The real problem here is that the debugging you've got isn't showing you what the problem is. The reason for this is because CoreData will put an array of NSError objects in the "top level" NSError object it returns if there is more than one problem (This is why you see error 1560, which indicates multiple problems, and an array of error 1570s). It appears that CoreData has a handful of keys it uses to stash information in the error it returns if there is an issue that will give you more useful information (Such as the entity the error occurred on, the relationship/attribute that was missing, etc). The keys you use to inspect the userInfo dictionary can be found in the reference docs here.

This is the block of code I use to get reasonable output from the error returned during a save:

    NSError* error;
    if(![[survey managedObjectContext] save:&error]) {
        NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
        NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
        if(detailedErrors != nil && [detailedErrors count] > 0) {
            for(NSError* detailedError in detailedErrors) {
                NSLog(@"  DetailedError: %@", [detailedError userInfo]);
            }
        }
        else {
            NSLog(@"  %@", [error userInfo]);
        }
    }

It will produce output that tells you the fields that are in missing, which makes fixing the problem significantly easier to deal with.

21
votes

I'm throwing this in as an answer, even though it's really more of an embellishment to Charles' snippet. The straight output from NSLog can be a mess to read and interpret, so I like to throw in some white space and call out the value of some critical 'userInfo' keys.

Here's a version of the method I've been using. ('_sharedManagedObjectContext' is a #define for '[[[UIApplication sharedApplication] delegate] managedObjectContext]'.)

- (BOOL)saveData {
    NSError *error;
    if (![_sharedManagedObjectContext save:&error]) {
        // If Cocoa generated the error...
        if ([[error domain] isEqualToString:@"NSCocoaErrorDomain"]) {
            // ...check whether there's an NSDetailedErrors array            
            NSDictionary *userInfo = [error userInfo];
            if ([userInfo valueForKey:@"NSDetailedErrors"] != nil) {
                // ...and loop through the array, if so.
                NSArray *errors = [userInfo valueForKey:@"NSDetailedErrors"];
                for (NSError *anError in errors) {

                    NSDictionary *subUserInfo = [anError userInfo];
                    subUserInfo = [anError userInfo];
                    // Granted, this indents the NSValidation keys rather a lot
                    // ...but it's a small loss to keep the code more readable.
                    NSLog(@"Core Data Save Error\n\n \
                      NSValidationErrorKey\n%@\n\n \
                      NSValidationErrorPredicate\n%@\n\n \
                      NSValidationErrorObject\n%@\n\n \
                      NSLocalizedDescription\n%@", 
                      [subUserInfo valueForKey:@"NSValidationErrorKey"], 
                      [subUserInfo valueForKey:@"NSValidationErrorPredicate"], 
                      [subUserInfo valueForKey:@"NSValidationErrorObject"], 
                      [subUserInfo valueForKey:@"NSLocalizedDescription"]);
                }
            }
            // If there was no NSDetailedErrors array, print values directly
            // from the top-level userInfo object. (Hint: all of these keys
            // will have null values when you've got multiple errors sitting
            // behind the NSDetailedErrors key.
            else {
                    NSLog(@"Core Data Save Error\n\n \
                      NSValidationErrorKey\n%@\n\n \
                      NSValidationErrorPredicate\n%@\n\n \
                      NSValidationErrorObject\n%@\n\n \
                      NSLocalizedDescription\n%@", 
                      [userInfo valueForKey:@"NSValidationErrorKey"], 
                      [userInfo valueForKey:@"NSValidationErrorPredicate"], 
                      [userInfo valueForKey:@"NSValidationErrorObject"], 
                      [userInfo valueForKey:@"NSLocalizedDescription"]);

            }
        } 
        // Handle mine--or 3rd party-generated--errors
        else {
            NSLog(@"Custom Error: %@", [error localizedDescription]);
        }
        return NO;
    }
    return YES;
}

This allows me to see the value for 'NSValidationErrorKey', which, when I encountered the issue from the OP, pointed directly to the non-optional Core Data entities that I'd forgot to set before trying to save.

0
votes

The problem touched me, when I save second record to CoreData. All not optional fields (relationship) was filled without nil as well, but in the error output I'd notice, that one of fields in first saved object had became nil. Strange a little? But the reason is quite trivial - one to one relationship which nullify first object, when I set it in the second.

So, the scheme is:

"Parent" with relationship "child" One to One
Create Child 1, set parent. Save - OK
Create Child 2, set parent. Save - Error, Child 1.Parent == nil
(behind the scene child 2 did nullify child 1 parent)

Changing the relationship in Parent from One to One to Many to One solved this task.

0
votes

I had transient property of type int that wasn't optional. Obviously, when it was set to 0, 1570 error appear. Just changed all my transient properties to optional. Nil-check logic can be implemented in code if necessary.

0
votes

I means that your model failed to validate, which could happen for a number of reasons: unused property in your model, missing value that's marked as required. To get a better understanding of what exactly went wrong, put a breakpoint in a place where you are ready to save your object, and call one of the validateFor... method variants, like:

po [myObject validateForInsert]

More detailed information about the problem is in error description. Successful validation means you will get no output.

0
votes

It helped me. Check this one too.

Check the optional box in your *.xcodatamodel objects