7

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];
                        }
                    }];
                }
            }
        }];
brunobasas
  • 383
  • 2
  • 14
  • Which context does the `deal` object belong to? It looks like it must be a different context from `private`. You shouldn't set relationships between objects in different contexts. Pass the managedObjectID and then retrieve it in the `private` context using `objectWithID`. – pbasdf Dec 27 '15 at 23:38
  • I checked already, but `deal` is created inside of the private context – brunobasas Dec 28 '15 at 07:46
  • so your problem is only with caregoryId = 10 ? 1st time you have 10 in dic[@"mainAttribute"] it find the category and 2nd time it doesn't? The fact that the problem might not be coming from MR_findFirstByAttribute tells me that the problem could be the string containing the integer in dic[@"mainAttribute"]. could you NSLog(@"ID:-%@-") in order to check that the ID doesn t have spaces before or after? – Mikael Jan 22 '16 at 03:34
  • @Mikael Is not related with strange spaces in the ID. i check it already. It seems that the object has a One-To-Many relationship as i can only attach one object to the relatuionship but it is Many-To-Many 100% sure i check it like there is no tomorrow. – brunobasas Jan 25 '16 at 17:24

1 Answers1

2

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.

SwiftArchitect
  • 47,376
  • 28
  • 140
  • 179
  • Of concern is the thread from which you execute `performBlockAndWait` for `self.model.context save`. That should be done on the main thread, or else your `UI` (say you are using `NSFetchedResultsController`) can crash (see http://stackoverflow.com/a/6913811/218152). Also, if you need to `[mainMOC save:&error]` every time you `[backgroundMOC save:&error]`, you may not need a `backgroundMOC` in the first place: the idea of `backgroundMOC` is to perform lengthy operations, which may not be the case here. More info: http://stackoverflow.com/search?q=user%3A218152+performBlockAndWait – SwiftArchitect Jan 26 '16 at 15:49