3

I'm working on an app where I have Users, each User have a many-to-many relationship to followers. I'm currently having some problems with saving new user objects. The app craches on managedObjectContext save. I get the following error:

Unresolved error Error Domain=NSCocoaErrorDomain Code=1560 "The operation couldn’t be   completed. (Cocoa error 1560.)" UserInfo=0x8580640 {NSDetailedErrors=(
"Error Domain=NSCocoaErrorDomain Code=1550 \"The operation couldn\U2019t be completed.  (Cocoa error 1550.)\" UserInfo=0x85820c0 {NSValidationErrorObject=<User: 0x7575870> (entity:  User; id: 0x75757c0 

NSValidationErrorKey=followers, NSLocalizedDescription=The operation couldn\U2019t be completed. (Cocoa error 1550.), NSValidationErrorValue=Relationship 'followers' on managed object (0x7575870) <User: 0x7575870> (entity: User; id: 0x75757c0 
"Error Domain=NSCocoaErrorDomain Code=1550 \"The operation couldn\U2019t be completed. (Cocoa error 1550.)\" UserInfo=0x8586830 {NSValidationErrorObject=<User: 0x7571ec0> (entity: User; id: 0x7571f00 

"Error Domain=NSCocoaErrorDomain Code=1550 \"The operation couldn\U2019t be completed. (Cocoa error 1550.)\" UserInfo=0x858e820 {NSValidationErrorObject=<User: 0x7573120> (entity: User; id: 0x7573160  

[...]

I can't really figure out what is causing this crash. My relationship looks like this:

enter image description here

enter image description here

The model have a custom NSManagedObject subclass with the @property (nonatomic, retain) NSSet *followers;. As I said, I'm not really sure what is causing this, so any guidance or ideas would be great!

Update

The app crashes in this method:

- (void)saveContext
{
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    [managedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
    if (managedObjectContext != nil) {
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You   should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            // Uncomment "abort" makes it work, but I still get the error. 
            abort();
        }
    }
} 

Update 2

More code from my models and how it use them:

How I set up my fetch request controller:

- (NSSortDescriptor *)sortDescriptorForFetchRequest
{
    NSSortDescriptor *sortDescriptor;
    if ([self.postType isEqualToString:@"following"] || [self.postType isEqualToString:@"followers"]) {
        sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"userId" ascending:NO];
    } else {
        sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"postId" ascending:NO];
    }

    return sortDescriptor;
}

- (NSEntityDescription *)entityForFetchRequest
{
    NSEntityDescription *entity;
    if ([self.postType isEqualToString:@"followers"] || [self.postType isEqualToString:@"following"]) {
        entity = [NSEntityDescription entityForName:@"User" inManagedObjectContext:[self.appController managedObjectContext]];
    } else {
        entity = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:[self.appController managedObjectContext]];
        }

    return entity;
}

- (void)setUpFetchResultController
{
    if (self.fetchedResultsController == nil) {
        NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
        [fetchRequest setEntity:[self entityForFetchRequest]];
        [fetchRequest setPredicate:[self predicateBasedOnPostType:self.postType]];
        NSArray *sortDescriptors = @[[self sortDescriptorForFetchRequest]];
        [fetchRequest setSortDescriptors:sortDescriptors];

        self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[self.appController managedObjectContext] sectionNameKeyPath:nil cacheName:[self cacheName]];
        self.fetchedResultsController.delegate = self;
    }
}

And my model, User:

+ (void)addUserFromDictionary:(NSDictionary *)dictionary forUser:(User *)user  inManagedObjectContext:(NSManagedObjectContext*)moc follower:(BOOL)follower following: (BOOL)following
{
    User *localUser;
    if (follower) {
        if ([dictionary isKindOfClass:[NSArray class]]) {
            NSEnumerator *enumerator = [dictionary objectEnumerator];
            id value;
            while (value = [enumerator nextObject]) {
                localUser = [self parseUserFromDictionary:value inManagedObjectContext:moc];

               if ([self user:localUser alreadyFollowingUser:user inManagedObjectContext:moc] == NO) {
                   [user addFollowersObject:localUser];
                   [localUser addFollowingObject:user];
              }   
            }
        }
    }
}

+ (User *)parseUserFromDictionary:(NSDictionary *)dictionary inManagedObjectContext:(NSManagedObjectContext*)moc
{
    NSNumberFormatter *numberFormatter= [[NSNumberFormatter alloc] init];
    NSNumber * userId = [numberFormatter numberFromString:(NSString *)[dictionary valueForKey:@"id"]];

    User *user;

    if ([self userAlreadyExist:userId inManagedObjectContext:moc] == NO) {
        NSEntityDescription *userDescription = [NSEntityDescription entityForName:@"User" inManagedObjectContext:moc];
        user = [[User alloc] initWithEntity:userDescription insertIntoManagedObjectContext:moc];
    } else {
        user = [User findUser:userId inManagedObjectContext:moc];
    }

    user.name = [dictionary valueForKey:@"name"];
    [...]    
    return user;
}
Anders
  • 2,903
  • 7
  • 58
  • 114
  • What does the relation User->following look like? It looks like you set some contraints which aren't satisfied – Robin van Dijke Mar 07 '13 at 21:40
  • It looks the same as followers. Wrong picture. – Anders Mar 09 '13 at 08:51
  • Are you sure you have cleaned you app and everything and that your sqlite file matches your defined xcdatamodeld file? I do not see an error in the code and scheme you provided. – Robin van Dijke Mar 11 '13 at 08:11
  • @RobinvanDijke Yes, I have tried both to delete the application and use "Reset contents...", but no difference. I think it's pretty strange that it's not working. I have used the same data model before, got this error recently. Could the error be caused outside the model? – Anders Mar 11 '13 at 18:16
  • And cleaned your XCode build before compiling? I'm not sure but the only causes I can think of are constraints on the relation User->Followers or not cleaned sqlite. As I mentioned, I cannot see the error in the code and screenshots you provided. If you want me to dig deeper into it you have to provide some more code/model-data which can contain the error – Robin van Dijke Mar 12 '13 at 07:36
  • Yes, cleaned and all. I have added some more code to my original question. – Anders Mar 12 '13 at 17:59
  • Are those the complete error messages? I've never seen them end without closing brackets and parentheses. If not, could you post the complete error? – Tom Harrington Mar 14 '13 at 16:50
  • @TomHarrington I have updated my original question now! – Anders Mar 14 '13 at 18:53
  • Thanks, but it's still hard to tell what's wrong. You might find this discussion useful though: http://www.cocoabuilder.com/archive/cocoa/190768-validation-error-in-relationship-in-coredata.html – Tom Harrington Mar 14 '13 at 21:32

3 Answers3

4

On a hunch: Is the relationship between objects in different MOCs? Strange things can happen if you do that!

tc.
  • 33,468
  • 5
  • 78
  • 96
3

I reproduced your code and datamodel in a separate project. I thought that it could be a cycle in your data or something so I tried all of the following code:

[userA addFollowersObject:userB];
[userB addFollowingObject:userA];

[userB addFollowersObject:userA];
[userA addFollowingObject:userB];

[userA addFollowersObject:userA];
[userA addFollowingObject:userA];

[userB addFollowingObject:userB];
[userB addFollowersObject:userB];

...but saves were valid all the time. The last thing I could think of is multithreading. Are you using multiple threads you are accessing your ManagedObjectContext from? If so, that could cause inconsistencies in your data which causes the error.

Robin van Dijke
  • 752
  • 4
  • 13
  • Thanks for your help! The secret was the ManagedObjectContext. I made a shared ManagedObjectContext for the app and now it works. – Anders Mar 16 '13 at 16:09
1

Would be helpful if you gave more details on the model definition including User and Followers. This is a guess but is your inverse for followers incorrect?

I would expect to see the relationship in User defined like:

M followers    User    following

and the relationship in Follower defined like:

M following    User    followers
XJones
  • 21,959
  • 10
  • 67
  • 82
  • Thaks, I actually only have one main model, `User` which have a many-to-many relationship with itself, `followers`. – Anders Mar 09 '13 at 22:24
  • I still think it is wrong for a relationship to have itself as the inverse. I made an edit to have followers/following be inverses to each other. – XJones Mar 09 '13 at 23:12
  • See http://stackoverflow.com/questions/12709842/ios-coredata-inverse-relationships-to-itself – XJones Mar 09 '13 at 23:12
  • Changed to your suggestion, but I still get the same error. But if I uncomment `abort();` from my save method, then it works but I get the same error log. I have added my save method to my original question. – Anders Mar 10 '13 at 12:18