I am looking for some help in getting a struct correct

I am looking for some help in getting a struct correct.

My goal is to build an iPad app for my mother, which contains all her family recipes.

In looking through her collection the content of her recipes falls into two types of structures:

  1. Unstructured : the recipe is a set of sentences that describe the method, ingredients and quantities.
  2. Structured : the recipe lists out the set of ingredients and quantities, and then a set of steps for the method. And there are some recipes that have two parts e.g. a pie filling and a pie crust, each containing different ingredients and methods.

In this attempt I am looking to take the approach of the News app i.e. a local JSON file for the content, but aspects from the Guidebook app that structures the app with models, services, etc.

The thing is, recipes that fall into a Chapter, let’s say Deserts, will have a mixture of Unstructured and Structured recipes, but they also share common elements such as name, serves, index etc. My first thought was to simply add in an unstructured element e.g. recipeAsBlurb, along with the other elements, making them all optional. (See the lines below starting with a >). But this doesn’t look to be the best approach. What if (somehow) the JSON file had content in all elements? What would the app do?

So my thinking is to create a ChapterRecipe object that contains all the common elements, but then instantiate a subclass of an UnstructRecipe or a StructRecipe, so the app’s RecipieDetailViewController can render the recipe that she taps on in either an unstructured fashion, or a structured one.

// MARK: - Chapter
struct Chapter: Codable {
    let chapterIndex: Int
    let chapterName: String
	let chapterBiline: String?
	let chapterImage: String?
    let chapterRecipes: [ChapterRecipe]
}

// Original struct
// MARK: - ChapterRecipe
struct ChapterRecipe: Codable {
    let recipeIndex: Int
    let recipeName: String
	let recipeServes: String?
    let recipePictureList: [RecipePictureList]?
>   let recipeAsBlurb: String?
>	let recipePartOneLabel: String?
>   let recipePartOneIngredientsList: [RecipePartOneIngredientsList]?
>   let recipePartOneStepsList: [RecipePartOneStepsList]?
>   let recipePartTwoLabel: String?
>   let recipePartTwoIngredientsList: [RecipePartTwoIngredientsList]?
>   let recipePartTwoStepsList: [RecipePartTwoStepsList]?
    let recipeOrigin: String?
}

// Improved struct
// MARK: - ChapterRecipe
struct ChapterRecipe: Codable {
    let recipeIndex: Int
    let recipeName: String
	let	recipePreamble: String?
	let recipeServes: String?
    let recipePictureList: [RecipePictureList]?
	**?????  How do a nest in UnstructRecipe and StructRecipe ??????**
    let recipeOrigin: String?
}

// MARK: - UnstructRecipe
struct UnstructRecipe: Codable {
    let recipeAsBlurb: String?
}

// MARK: - StructRecipe
struct StructRecipe: Codable {
	let recipePartOneLabel: String?
    let recipePartOneIngredientsList: [RecipePartOneIngredientsList]?
    let recipePartOneStepsList: [RecipePartOneStepsList]?
    let recipePartTwoLabel: String?
    let recipePartTwoIngredientsList: [RecipePartTwoIngredientsList]?
    let recipePartTwoStepsList: [RecipePartTwoStepsList]?
}

I am wondering if it is as simple as adding two lines as follows

// Improved struct
// MARK: - ChapterRecipe
struct ChapterRecipe: Codable {
    let recipeIndex: Int
    let recipeName: String
	let recipePreamble: String?
	let recipeServes: String?
    let recipePictureList: [RecipePictureList]?
	let unstructRecipe: [UnstructRecipe]?
	let structRecipe: [StructRecipe]?
    let recipeOrigin: String?
}

Here’s what I would do…

From your description, it sounds like there’s really little difference between structured and unstructured recipes besides the ingredients list. After that, one is “a set of sentences” while the other is “a set of steps”; this implies the latter is likely numbered. So combine the two into a single Recipe struct and indicate with a member of that struct whether the recipe is structured or unstructured. Then when you go to display the recipe, you can test the value of isStructured to know whether to display numbers or not.

As for multiple parts to a recipe, you can nest an optional list of Recipe objects inside the Recipe struct to handle those since each one would be like a little recipe unto itself.

So something like this:

struct Recipe : Codable {
    let name : String
    let preamble : String?
    let serves : String?
    let pictures : [PictureList]?
    let isStructured : Bool
    let steps : [String]
    let ingredients : [Ingredient]?
    let subRecipes : [Recipe]?
}

struct Ingredient : Codable {
    let name : String
    // NOTE : I'd prefer to do the next two members as a 
    //        Measurement<UnitVolume> type, but couldn't figure
    //        out how to get that to work with Codable
    let quantity : Double
    let unit : String
    let notes : String?
}

Seasons greetings.Thanks for your suggestion. I am going to take a few days to think about it (coz it’s Christmas) with the hope to find a path forwrd. Cheers

Your suggestion of using a bool was a good one. However, when I ran it passed a software eng. at work, they suggested that using an enum would be a more extensible solution. So this morning I was able to parse the local Json file successfully (happy happy joy joy) by doing the following:

// MARK: - Recipe
struct Recipe: Decodable {
let recipeIndex: Int
let recipeName: String
let recipePartOneLabel: String?
let recipePartOneIngredientsList: [RecipePartOneIngredientsList]?
let recipePartOneStepsList: [RecipePartOneStepsList]?
let recipePartTwoLabel: String?
let recipePartTwoIngredientsList: [RecipePartTwoIngredientsList]?
let recipePartTwoStepsList: [RecipePartTwoStepsList]?
let recipePartThreeLabel: String?
let recipePartThreeIngredientsList: [RecipePartThreeIngredientsList]?
let recipePartThreeStepsList: [RecipePartThreeStepsList]?
let recipeAsBlurb: String?
var recipeType: RecipeType
}

and to make enum conform to Decodable protocol

// MARK: - Recipe Type enum to handle two types of recipe formats
enum RecipeType: String, Decodable {
case Structured
case Unstructured
}

Merry Christmas

The ultimate solution is different to the above (which ultimately wouldn’t work). Here’s a snippet of what it ultimately is. Now I just have to learn how to code for enums and CodingKey!

// MARK: - Cookbook
struct Cookbook: Codable {
let totalChapters: Int
let totalRecipes: Int
let chapters: [Chapter]
}

// MARK: - Chapter
struct Chapter: Codable {
let chapterIndex: Int
let chapterName: String
let chapterImage: String
let recipes: [Recipe]
}

// MARK: - Recipe
struct Recipe: Codable {
let recipeIndex: Int
let recipeName: String
let recipeType: RecipeType
}

// MARK: - RecipeType
struct RecipeType: Codable {
let structured: [Structured]?
let unstructured: Unstructured?

enum CodingKeys: String, CodingKey {
    case structured = "Structured"
    case unstructured = "Unstructured"
}

}

// MARK: - Structured
struct Structured: Codable {
let recipePartOneLabel: String?
let recipePartOneIngredientsList: RecipePartOneIngredientsList
let recipePartOneStepsList: RecipePartOneStepsList
let recipePartTwoLabel: String?
let recipePartTwoIngredientsList: RecipePartTwoIngredientsList?
etc…