Question

I'm doing my head in trying to figure out an issue I'm having with core-data. I understand what the error means, and I've had it before (and fixed it) but I can't figure out why I'm getting it now.

My app has the following structure :

MPModel -> MPPlace and MPModel -> MPProject

Where MPModel is a subclass of NSManagedObject and MPPlace and MPProject are subclasses of MPModel.

The data model has a relationship between MPPlace and MPProject where MPPlace has-many MPProjects and MPProject belongs-to MPPlace.

When the app loads, it fetches a number of MPPlace objects which works perfectly. When a user selected a place and then selects the projects option, the app attempts to retrieve the list of projects. This is where the app fails however, with the following error

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason:
'Failed to process pending changes before save.  The context is still dirty after 100 
attempts.  Typically this recursive dirtying is caused by a bad validation method, 
willSave, or notification handler.'

Each MPModel contains a number of custom methods, including saveAllLocally which simply saves an array of objects to the persistence store if they don't already exist, and loadNestedResources which fetches new objects from the server that are related to the current object. I am finding that the failure is occurring with the loadNestedResources method but I can't figure out why.

The loadNestedResources method looks like this :

- (void) loadNestedResourcesOfType:(Class)type atURL:(NSString *)resURL withBlock:(MPFindObjectsBlock)block
{
    if (![type isSubclassOfClass:[MPModel class]]) {
        block(nil, nil);
    } else {
             NSString *url = [NSString stringWithFormat:@"%@%@/%@%@", kMP_BASE_API_URL, [self.class baseURL], self.objID, [type baseURL]];
        MPRequest *request = [[MPRequest alloc] initWithURL:url];

        // Attempt to see if we already have this relation
           NSString *relation = [[MPUtils stripClassName:type].lowercaseString stringByAppendingString:@"s"];
        NSSet *relatedObjects = [self valueForKey:relation];

        if (relatedObjects && relatedObjects.count > 0) {
            // We have some objects so lets exclude these from our request
            NSMutableArray *uuids = [NSMutableArray arrayWithCapacity:0];
            for (MPModel *obj in relatedObjects) {
                [uuids addObject:obj.uuid];
            }

            [request setParam:[uuids componentsJoinedByString:@";"] forKey:@"exclude_uuids"];
        }

        [MPUser signRequest:request];
        [request setRequestMethod:@"POST"];
        [request submit:^(MPResponse *resp, NSError *error) {
            if (error) {
                if (relatedObjects.count > 0) {
                    block([relatedObjects allObjects], nil);
                } else {
                    block(nil, error);
                }
            } else {
                // Combine all of our objects
                NSMutableArray *allObjects = [[type createListWithResponse:resp] mutableCopy];
                if (allObjects.count > 0) {
                    [allObjects addObjectsFromArray:[relatedObjects allObjects]];

                    // Make sure they're now all saved in the persistence store
                    NSArray *savedObjects = [MPModel saveAllLocally:allObjects forEntityName:NSStringFromClass(type)];
                    for (NSObject *obj in savedObjects) {
                        [obj setValue:self forKey:[MPUtils stripClassName:self.class].lowercaseString];
                    }

                    // Set them as our related objects for this relationship
                    [self setValue:[NSSet setWithArray:savedObjects] forKey:relation];

                    [MPModel saveAllLocally:savedObjects forEntityName:NSStringFromClass(type)];
                    block(allObjects, nil);
                } else {
                    block([relatedObjects allObjects], nil);
                }
            }

        }];
    }
}

The methods runs perfectly right up until the second call to saveAllLocally which is where I get the error. The MPModel class also uses the willSave method, which has the following :

- (void) willSave
{
    NSDate *now = [NSDate date];

    if (!self.updated_at) {
        self.updated_at = now;
    }

    if (!self.created_at) {
        self.created_at = now;
    }

    if (!self.uuid) {
        self.uuid = [self createUUID];
    }

    if (!self.last_sync) {
        self.last_sync = now;
    }

    if ([self isUpdated] && self.changedValues.count > 0) {

        if (!self.attribute_updated_at) {
            NSDictionary *attributes = [self.entity attributesByName];
            NSMutableDictionary *dates = [NSMutableDictionary dictionaryWithCapacity:0];
            for (NSString *attr in attributes.allKeys) {
                [dates setObject:now forKey:attr];
            }

            [self setAttributeUpdatedDates:dates];
        }

        if (_updatedAtSet) {
            _updatedAtSet = NO;
        } else {
            if ([self.changedValues.allKeys containsObject:@"last_sync"]) {
                self.updated_at = [self.changedValues objectForKey:@"last_sync"];
            } else {
                self.updated_at = [NSDate date];
            }

                         _updatedAtSet = YES;
                 NSDictionary *changed = [self changedValues];
            NSMutableDictionary *dates = [[self attributeUpdatedDates] mutableCopy];

            for (NSString *key in changed.allKeys) {
                [dates setObject:now forKey:key];
            }

            [self setAttributeUpdatedDates:dates];
        }

    }
}

From what I can gather, this should be fine as it shouldn't be setting any more values if the _updatedAtSet variable is set to true, however it is still breaking and I cannot figure out why!

Please can someone help me Thanks

Was it helpful?

Solution

Have solved it.

I just moved the _updatedAtSet = NO; into the didSave method rather than where it is and it's working fine now. Thanks anyway

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top