Rillieux
Rillieux

Reputation: 707

Can I use @FetchRequest with a @State var in a Picker()?

I cannot figure out how to tie in the @State var to a picker so that @FetchRequest will update.

This code compiles, but changing the picker selection does nothing to fetchRequest, because it's not calling the init. All kinds of other variants have failed mostly.

How do I accomplish this?

    struct ContentView: View {
        @Environment(\.managedObjectContext) private var viewContext
        @State private var selectedSkill: Skill? = nil
        @State private var pickerSelection: Int
        @FetchRequest var skills: FetchedResults<Skill>
        
        init(pickerSelection: Int) {
            self._pickerSelection = State(initialValue: pickerSelection)
            self._skills = FetchRequest(
                entity: Skill.entity(),
                sortDescriptors: [NSSortDescriptor(keyPath: \Skill.value, ascending: true)],
                predicate: NSPredicate(format: "apparatus == %d", pickerSelection)
            )
        }

Upvotes: 1

Views: 396

Answers (1)

Jevon Charles
Jevon Charles

Reputation: 684

There are a few ways to go about this here is mine. Not sure of your intended use of Skill but I think you can figure out how to make it work for you.

I would make apparatus an enum

enum Apparatus: Int, CaseIterable, CustomStringConvertible, Identifiable {
  case vault, unevenBars, balanceBeam, all

  var id: String { self.description }
  var description: String {
    switch self {
        case .vault: return "Vault"
        case .unevenBars: return "Uneven Bars"
        case .balanceBeam: return "Balance Beam"
        case .all: return "All"
    }
  }
}

extend on Item

extension Item {
  var unwrappedApparatus: Apparatus {
    Apparatus(rawValue: Int(apparatus)) ?? Apparatus.vault
  }

  var unwrappedName: String {
    name ?? "Unknown"
  }
}

Set your @State in ContentView

struct ContentView: View {
  @Environment(\.managedObjectContext) private var viewContext
  @State var selectedApparatus = Apparatus.vault

  var body: some View {
    List {
        Picker("Apparatus", selection: $selectedApparatus) {
            Text("Vault").tag(Apparatus.vault)
            Text("Uneven Bars").tag(Apparatus.unevenBars)
            Text("Balance Beam").tag(Apparatus.balanceBeam)
            Text("All").tag(Apparatus.all)
        }

        Text(selectedApparatus.description)

        ItemsView(selectedApparatus: $selectedApparatus) // <-- View changing on selectedApparatus
    }
  }
}

Now break out the View you want to change based on the selectedApparatus to its own View

struct ItemsView: View {
  @FetchRequest private var items: FetchedResults<Item>

  init(selectedApparatus: Binding<Apparatus>) {
    self._items = FetchRequest(entity: Item.entity(),
                               sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true)],
                               predicate: selectedApparatus.wrappedValue == .all ? nil : NSPredicate(format: "apparatus == %d", selectedApparatus.wrappedValue.rawValue))
}

  var body: some View {
    ForEach(items) { item in
        Text(item.unwrappedName)
    }
  }
}

Upvotes: 1

Related Questions