swiftenumsswiftuipicker

SwiftUI - using all but one values of Enum for different Pickers


In my app, I need to use a Picker in two different places. In one place, it's to set a property to one of three values: A, B, or C. But, in another place, I want to use the picker to filter a array of items to show either all items (no filter) or only the A, B, or C items.

So, my first attempt was to create an enum like so:

enum Category {
  case all
  case A
  case B
  case C
}

Using this, I can filter my list successfully. But, when I try to create a new item in the array, I don't want all to be an option. The user should only see a picker with A, B, and C.

I could try:

enum Category {
  case A
  case B
  case C
}

but then how do I add the all option for the filter picker?

A tournament can be a men's, women's, or mixed tournament. On the screen where I list the tournaments, I want to be able to show all tournaments or just the men's, just the women's or just the mixed tournaments. So, I have a picker that spans the width of the iPhone. Looks and works great.

Obviously, when adding a new item to the list, I have to specify its category, but not "all". Also a picker.

So, in one case, I need three values, in the other case, I need the same three values with "All" added at the beginning.


Solution

  • You should define your enum without the all case, because all is not a valid Category for an item. (This is a programming guideline known as “make illegal states unrepresentable”.)

    enum Category: Hashable, CaseIterable {
        case a
        case b
        case c
    }
    

    With that definition, the Picker for setting an item's property can look like this:

    Picker("Category", selection: $category) {
        ForEach(Category.allCases, id: \.self) { category in
            Text(verbatim: "\(category)")
                .tag(category)
        }
    }
    

    Then you should recognize that your filter is optional. You can filter by item category, or you can perform no filtering. So your filter property should be declared optional:

    @Binding var categoryFilter: Category?
    

    The Picker for setting the filter then needs to be careful to use optional tags:

    Picker("Category Filter", selection: $categoryFilter) {
        Text("None")
            .tag(Category?.none)
        ForEach(Category.allCases, id: \.self) { category in
            Text(verbatim: "\(category)")
                .tag(Category?.some(category))
        }
    }