Search code examples
iphoneobjective-ccore-dataxcode4

Core Data Errors 1600 and 133020


I think I'm coming to my wits end with this. I'm working on an app that executes and api call and then stores the response within Core Data. I'm also pretty n00b at iphone development, so if my question could use more information, please let me know.

It seems like whenever I run the app, and the API is called for the first time, it works fine, but when it updates the data already in core data, I get either error 130020 or 1600. I looked up what they are:

1600 - NSValidationRelationshipDeniedDeleteError 133020 - NSManagedObjectMergeError

From what I read a 1600 can occur when a deleteRule is set to deny, but each of my relationship delete rules are set as nullify in the data model.

And I have no idea what to do at all about 133020.

The other interesting thing to note is that there doesn't seem to be any logic (that I can determine) as to when these errors pop up. One time it could be 1600, one time 133020, and another time it could be absolutely nothing, for the same operation within the same sequence.

If someone could give me some pointers as to what I can look at, or even how I can try to debug this, I would appreciate it greatly.

I pasted below 2 sections of the code which seem to consistently cause at least one of the errors:

- (void)updatePhotoAlbumsForGroup:(NSNumber *)gid notifyRegistrant:(id <APRegistrant>)registrant {
  NSManagedObjectContext *context = [[self newContextWithSharedCoordinator] autorelease];
  [self registerObserver:registrant selector:@selector(managedObjectContextDidSave:) forDidSaveNotificationFromContext:context];

  APGroup *group = [APCoreDataRequests fetchGroupWithGID:gid inContext:context];

  // Download and create new albums
  [self queueBlock:^{
    NSArray *drupalAlbums = [_drupalRequests getPhotoAlbumsForGroup:gid];
    if ([drupalAlbums count]) {
      [self refreshPhotoAlbumsForGroup:group withDrupalAlbums:drupalAlbums inContext:context];

      // Save any changes
      dispatch_async(dispatch_get_main_queue(), ^{
        [self saveContext:context];
      });
    }
  }];
}

And here is the refreshphotoalbumsforgroup function that is called above:

/**
 Uses the drupal albums to create new albums, update existing ones, and delete dead ones.
 */
- (void)refreshPhotoAlbumsForGroup:(APGroup *)group withDrupalAlbums:(NSArray *)drupalAlbums inContext:(NSManagedObjectContext *)context {
  NSMutableArray *newDrupalAlbums = [NSMutableArray arrayWithArray:drupalAlbums];
  NSMutableArray *existingAlbumsToDelete = [NSMutableArray array];

  // Update existing albums and collect albums to be deleted and created
  if (group.photoAlbums) {
    for (APPhotoAlbum *album in group.photoAlbums) {
      BOOL deleteAlbum = YES;

      for (NSDictionary *drupalAlbum in drupalAlbums) {
        NSNumber *aid = [drupalAlbum objectForKey:kGetPhotoAlbumsAIDKey];
        if (aid) {
          if ([aid isEqualToNumber:album.aid]) {
            deleteAlbum = NO;

            // Update existing album
            NSString *title = [drupalAlbum objectForKey:kGetPhotoAlbumsTitleKey];
            if (title) album.title = title;
            NSString *repPhotoURL = [drupalAlbum objectForKey:kGetPhotoAlbumsRepresentativePhotoURLKey];
            if (repPhotoURL) album.representativePhotoURL = repPhotoURL;
            NSNumber *numPhotos = [drupalAlbum objectForKey:kGetPhotoAlbumsNumberOfPhotosKey];
            if (numPhotos) album.numberOfPhotos = numPhotos;
            NSNumber *dateNum = [drupalAlbum objectForKey:kGetPhotoAlbumsModificationDateKey];
            if (dateNum) album.modificationDate = [NSDate dateWithTimeIntervalSince1970:[dateNum doubleValue]];
            NSNumber *radioactivity = [drupalAlbum objectForKey:kGetPhotoAlbumsRadioactivityKey];
            if (radioactivity) album.radioactivity = radioactivity;

            [newDrupalAlbums removeObject:drupalAlbum];
            break;
          }
        }
        else {
          [newDrupalAlbums removeObject:drupalAlbum];
        }
      }

      if (deleteAlbum) {
        [existingAlbumsToDelete addObject:album];
      }
    }
  }

  // Delete albums
  for (APPhotoAlbum *album in existingAlbumsToDelete) {
    [context deleteObject:album];
  }

  // Create and add new albums
  NSMutableSet *currentAlbums = [NSMutableSet setWithSet:group.photoAlbums];
  for (NSDictionary *drupalAlbum in newDrupalAlbums) {
    NSNumber *aid = [drupalAlbum objectForKey:kGetPhotoAlbumsAIDKey];
    APPhotoAlbum *album = [APCoreDataRequests fetchPhotoAlbumWithAID:aid inContext:context];

    // Set album properties
    NSString *title = [drupalAlbum objectForKey:kGetPhotoAlbumsTitleKey];
    if (title) album.title = title;
    NSString *repPhotoURL = [drupalAlbum objectForKey:kGetPhotoAlbumsRepresentativePhotoURLKey];
    if (repPhotoURL) album.representativePhotoURL = repPhotoURL;
    NSNumber *numPhotos = [drupalAlbum objectForKey:kGetPhotoAlbumsNumberOfPhotosKey];
    if (numPhotos) album.numberOfPhotos = numPhotos;
    NSNumber *dateNum = [drupalAlbum objectForKey:kGetPhotoAlbumsModificationDateKey];
    if (dateNum) album.modificationDate = [NSDate dateWithTimeIntervalSince1970:[dateNum doubleValue]];
    NSNumber *radioactivity = [drupalAlbum objectForKey:kGetPhotoAlbumsRadioactivityKey];
    if (radioactivity) album.radioactivity = radioactivity;

    [currentAlbums addObject:album];
  }
  group.photoAlbums = [NSSet setWithSet:currentAlbums];
}

Solution

  • Try setting the mergePolicy of your managed object context, e.g. to NSMergeByPropertyObjectTrumpMergePolicy. It defaults to NSErrorMergePolicy which always causes a validation error in case multiple contexts edit the same object.