Self-referencing Data Object In Coredata
Solution 1:
Create a relationship "nextStep" from the entity to itself. Then you can just do something like
// Create first thing:
Thing *thingA = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:context];
thingA.name = @"...";
// Create second thing:
Thing *thingB = [NSEntityDescription insertNewObjectForEntityForName:@"Thing" inManagedObjectContext:context];
thingB.name = @"...";
// Establish the relationship between these two objects:
thingA.nextStep = thingB;
"nextStep" should also have an "inverse relationship". Since two or more objects can have the same successor (as in your case, where both "C" and "D" point to "E"), the inverse relationship would be a "to-many" relationship, and could be called "previousSteps" or similar.
In the Core Date model editor, this would look like:

Solution 2:
Ah! You provided the vital clue, Martin.
I tried the code sample, but that didn't quite work. It ended up creating duplicates of everything because both thingA and thingB got inserted into the table. However, the diagram actually gave me what I think might be the key. Previously I had tried assigning nextStep as its own inverse relationship, which was just bonkers. But just adding previousSteps and setting that to Many while nextStep was set to One seems to have led to the solution.
Here's what I ended up creating for the relationships:
(source: justinwhitney.com)
Here is the plist I used to populate the Steps entity (intended to be run on first-time use or when the database is reset):
(source: justinwhitney.com)
And now here is the entity population routine. This is what was tripping me up yesterday:
- (IBAction)populateSteps:(UIButton *)sender {
NSString *responseString;
BOOL isStepsPopulated = [[UserPrefs loadUserData:@"didPopulateSteps"] boolValue];
if (!isStepsPopulated) {
NSDictionary *stepDict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"DefaultSteps" ofType:@"plist"]];
NSArray *stepArray = [stepDict objectForKey:@"Steps"];
//1
__block NSMutableDictionary *stepObjectDict = [[NSMutableDictionary alloc] initWithCapacity:[stepArray count]];
//2
[stepArray enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
//3
Steps *thisStep = [NSEntityDescription insertNewObjectForEntityForName:@"Steps" inManagedObjectContext:self.managedContext];
thisStep.stepName = [dict objectForKey:@"StepName"];
//4
[stepObjectDict setObject:thisStep forKey:thisStep.stepName];
}];
//5
[stepArray enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop) {
Steps *thisStep = [stepObjectDict objectForKey:[dict objectForKey:@"StepName"]];
Steps *nextStep = [stepObjectDict objectForKey:[dict objectForKey:@"NextStep"]];
thisStep.nextStep = nextStep;
}];
NSError *error = nil;
[self.managedContext save:&error];
if (error) {
responseString = [NSString stringWithFormat:@"Error populating Steps: %@", error.description];
} else {
responseString = @"Steps have been populated.";
[UserPrefs saveUserData:[NSNumber numberWithBool:TRUE] forKey:@"didPopulateSteps"];
}
} else {
responseString = @"Steps already populated.";
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Populate Steps" message:responseString delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
}
Here's the key. At (1) create an NSMutableDictionary to hold the results of the iteration, using the stepName as the Key so it can be referred to later on.
While (2) enumerating through the plist contents, (3) create the first NSEntityDescription just as you did. But instead of creating the second one, (4) add it to the dictionary.
Once the initial enumeration is done, (5) go back through a second time. But this time, create the relationships by referring to the original objects themselves. Make sense?
After that, save the context as normal. The result, in this case, is 5 records, each referencing another record in the same entity, with no conflicts or duplicates.
(source: justinwhitney.com)
Now the important question: how do I mark this as answered? Who gets the checkmark?
Post a Comment for "Self-referencing Data Object In Coredata"