Akaino Akaino - 6 months ago 18
Swift Question

Core Data many to many relation with intermediate table (Swift 2)

TL;DR EDIT with answer

As Wain perfectly answered this is how I get my information now:

let ingredientsToRecipe = recipe.valueForKey("ingredientsToRecipe")! as! NSSet
for i in ingredientsToRecipe {
print(i.valueForKey("amount")!)
print(i.valueForKeyPath("ingredient.name")!)
}


Original question

I have a huge problem understanding the usage of intermediate tables in CoreData. I've searched SO for answers and found a few threads about intermediate tables and many-to-many relations but those where either Objective-C or didn't help me.

I have the following setup (simplified):
image

Now I want to add a new recipe with a bunch of ingredients.
Let's say a Burger. The burger consists of


  • 1 cocumber,

  • 1 Tomato,

  • 1 Meat,

  • 2 bread

    (yummy...)



This is what I tried so far:

// Core Data
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let entity = NSEntityDescription.entityForName("Recipe",
inManagedObjectContext:managedContext)
// creating a new recipe with name and id
let recipe = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext: managedContext)
recipe.setValue("Burger", forKey: "name")
recipe.setValue("B_001", forKey: "id")


Now I got an
Array:[NSManagedObject]
of of ingredients (created just like the Burger) and a
Dictionary
of amounts to the ingredient_IDs. This is how I'm trying to marry my Recipe with the ingredients (over the intermediate table).

for i in selectedIngredients { // the ingredient array
let ingredientsToRecipe = NSEntityDescription.insertNewObjectForEntityForName("RecipeIngredient", inManagedObjectContext: managedContext)

ingredientsToRecipe.setValue(i, forKey: "ingredient")
ingredientsToRecipe.setValue(recipe, forKey: "recipe")

let quantity = Double(quantityDictionary[(i.valueForKey("id") as! String)]!) // the amount-to-ID dictionary
ingredientsToRecipe.setValue("\(quantity)", forKey: "quantity")
}


In the end I simply save everything:

do {
try managedContext.save()
print("Saved successfully")
self.dismissViewControllerAnimated(true, completion: nil)
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}


All this above somehow works. But now I'm struggling to fetch information about my recipes.
How am I supposed to fetch the amount of tomatoes of this specific burger?

Things like
recipe.valueForKey("RecipeIngredient").valueForKey("amount")
work but I don't know which amount is from which ingredient.
Am I doing anything wrong?
What can/should I do better?

The goal is to create a recipe with ingredients and later populate a Table with information about the recipe and the amounts of it's ingredients (and the ingredients themselves).

I appreciate any help!

Answer

The power of the intermediate object is that it takes your many-to-many relationship and breaks it into multiple one-to-many relationships. The to-one relationships are easy to navigate.

So, from your Recipe you can get an array of RecipeIngredients, and for each one you can get valueForKey("amount") and valueForKeyPath("ingredient.name").

Comments