How to access second-level nested enum passed as parameter in Swift

Viewed 119

Trying to access the value of a double-nested enum in Swift that is passed in as a parameter to the first enum. I'm not sure how to access the CutStyle; within the for loop in shareSalad, each ingredient that is iterated over does not have a .dot accessor except self. I.e. there is no ingredient.cut or ingredient(cut) available.

I'm confused on how to access CutStyle

import Cocoa

enum Ingredients {
    case lettuce(cut: CutStyle)
    case tomatoes(cut: CutStyle)
    case onions(cut: CutStyle)
    case cucumbers(cut: CutStyle)
    case dressing
    
    enum CutStyle {
        case diced
        case chopped
        case minced
        case grated
    }
}

var salad: [Ingredients] = []

func makeSalad(with ingredient: Ingredients) {
    switch ingredient {
    case .lettuce(let cut):
        salad.append(.lettuce(cut: cut))
    case .tomatoes(let cut):
        salad.append(.tomatoes(cut: cut))
    case .onions(let cut):
        salad.append(.onions(cut: cut))
    case .cucumbers(let cut):
        salad.append(.cucumbers(cut: cut))
    case .dressing(let cut):
        salad.append(.dressing(cut: cut))
    }
}

func shareSalad(my salad: [Ingredients]) {
    print("My salad contains:")
    for ingredient in salad {

        // Q: How to access the ingredient CutStyle here?
        print(ingredient)
    }
}

makeSalad(with: .cucumbers(cut: .chopped))
makeSalad(with: .onions(cut: .diced))
makeSalad(with: .tomatoes(cut: .minced))

shareSalad(my: salad)

/*
My salad contains:
cucumbers(cut: __lldb_expr_29.Ingredients.CutStyle.chopped)
onions(cut: __lldb_expr_29.Ingredients.CutStyle.diced)
tomatoes(cut: __lldb_expr_29.Ingredients.CutStyle.minced)
*/
1 Answers

You would need a giant switch statement to extract the cut styles,

for ingredient in salad {
    let cutStyle: CutStyle?
    switch ingredient {
    case .lettuce(let cut):
        cutStyle = cut
    case .tomatoes(let cut):
        cutStyle = cut
    case .onions(let cut):
        cutStyle = cut
    case .cucumbers(let cut):
        cutStyle = cut
    case .dressing:
        cutStyle = nil
    }
    print(cutStyle)
}

I think you could have modelled your salad better. Here are two alternative designs that allows you to access the cut style more easily.

One way is to make a SaladIngredient struct that composes a cut style and an ingredient:

struct SaladIngredient {
    enum Ingredient {
        case lettuce
        case tomatoes
        case onions
        case cucumbers
        case dressing
    }

    enum CutStyle {
        case diced
        case chopped
        case minced
        case grated
    }

    let ingredient: Ingredient
    let cutStyle: CutStyle?
}
func shareSalad(my salad: [SaladIngredient]) {
    print("My salad contains:")
    for ingredient in salad {
        if let cutStyle = ingredient.cutStyle {
            print(cutStyle)
        }
    }
}

The disadvantage of this is that it's harder to enforce the rule that dressings don't have a cut style.

Alternatively, collapse all those enum cases with a cut style into one case:

enum SaladIngredient {
    enum Ingredient {
        case lettuce
        case tomatoes
        case onions
        case cucumbers
    }
    enum CutStyle {
        case diced
        case chopped
        case minced
        case grated
    }
    case cuttable(Ingredient, cutStyle: CutStyle)
    case dressing
}
func shareSalad(my salad: [SaladIngredient]) {
    print("My salad contains:")
    for ingredient in salad {
        if case .cuttable(_, cutStyle: let cutStyle) = ingredient {
            print(cutStyle)
        }
    }
}

The disadvantage of this is that you are making .dressing a totally different "type" of thing from lettuce, tomatoes and others.

Related