Search code examples
iosobjective-ccore-datarelationshipnsfetchrequest

Not found an object in coredata after added to a relationship


I have an issue with the fetch of an object after added in a relationship. The first time that i fetch the category, always found, then when i added to the relationship the following categories not found.

The relationship is a Many-To-Many.

Example:

  • Fetch category with categoryId = 10
  • Found category object
  • Added to the parent object relationship
  • Next object
  • If the several categories has the same id, categoryId = 10, not found

    NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    
       [private setParentContext:self.model.context];
        __block NSError *error = nil;
    
        [private performBlockAndWait:^{
    
            GPDeal *deal = [EKManagedObjectMapper objectFromExternalRepresentation:dic withMapping:[GPDeal objectMapping] inManagedObjectContext:private];
            for (NSDictionary *dic in responseObject[@"response"]) {
    
                GPCategory *category;
    
               //The first time always found
                if ((category = [GPCategory MR_findFirstByAttribute:@"catId" withValue:dic[@"mainAttribute"] inContext:private])) {
                    NSLog(@"Found");
                    [category addDealsObject:deal];
    
                } else {
                    NSLog(@"Not Found");
    
                }
    
            }
        }];
    
        NSError *PrivateError = nil;
        if (![private save:&PrivateError]) {
            NSLog(@"Unresolved error %@, %@", PrivateError, [PrivateError userInfo]);
            abort();
        }
    
        if (!error) {
            //Save on main moc
            [self.model saveWithErrorBlock:^(BOOL success, NSError *error) {
                if (!success) {
                    NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
                }
            }];
    
        } else {
            NSLog(@"Error saving context: %@\n%@", [error localizedDescription], [error userInfo]);
        }
    

EDIT:

Solved , I guess my problem was that i forgot to save the main context in the end of each iteration .

        NSManagedObjectContext *backgroundMOC = [self.model backgroundMOC:self.model.context];

        [backgroundMOC performBlockAndWait:^{

            for (NSDictionary *dic in responseObject[@"response"]) {

                GPDeal *deal = [EKManagedObjectMapper objectFromExternalRepresentation:dic withMapping:[GPDeal objectMapping] inManagedObjectContext:backgroundMOC];

                GPCategory *category;
                if ((category = [GPCategory MR_findFirstByAttribute:@"catId" withValue:dic[@"mainAttribute"] inContext:backgroundMOC])) {
                    NSLog(@"Found with mainAttribute %@", dic[@"mainAttribute"]);
                    [deal addDealCategoryObject:category];
                }

                if([backgroundMOC hasChanges]) {
                    NSError * error;
                    [backgroundMOC save:&error];

                    [self.model.context performBlockAndWait:^{
                        if([self.model.context hasChanges]) {
                            NSError * error;
                            [self.model.context save:&error];
                        }
                    }];
                }
            }
        }];

Solution

  • You may lack saving the MOC chain. For clarity, I have replaced the keyword private with the variable name backgroundMOC.

    On the question above, I can only assume that the line NSManagedObjectContext *private = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; is technically similar to:

    - (NSManagedObjectContext *)backgroundMOC:(NSManagedObjectContext *)mainMOC
    {
        NSManagedObjectContext * threadManagedObjectContext = [[NSManagedObjectContext alloc]
                                 initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [threadManagedObjectContext setParentContext:mainMOC];
        [threadManagedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
        return threadManagedObjectContext;
    }
    

    passing self.model.context as mainMOC, with a self.model.context and a setMergePolicy.
    Similarly, I must assume that self.model saveWithErrorBlock is technically identical to:

    [mainMOC performBlockAndWait:^{
        if([mainMOC hasChanges]) {
            NSError * error;
            [mainMOC save:&error];
            // handle error
        }
    }];
    

    If so, the same should be said of the backgroundMOC (your private reference):

    [backgroundMOC performBlockAndWait:^{
        if([backgroundMOC hasChanges]) {
            NSError * error;
            [backgroundMOC save:&error];
            // handle error
        }
    }];
    

    In other words, you want to ensure that your backgroundMOC and mainMOC save operation is executed from their respective threads, with performBlockAndWait.