Apologies because I'm new to SwiftUI -
I have a class (Meal) that contains an array of objects of another class (Food). What I'm trying to achieve here is to be able to add foods and have that update in a list, and then to be able to edit an individual food by tapping on it.
I'm iterating through the list of foods and using each food to (1) create a NavigationLink and (2) simply list the food's name in the list. The problem is that I get the error Initializer 'init(_:)' requires that 'Binding<String>' conform to 'StringProtocol'
when I try to reference the food in an "unbound" way. Specifically, in the code below, it's at the line Text(food.name)
.
ForEach($meal.foods) { food in
NavigationLink(destination: FoodView(food: food)) {
Text(food.name)
}
}
I'm aware I could iterate through a list of indices instead (I think) but I think that's probably bad practice. I'm trying to figure out what the best practice is in this scenario. Thanks in advance!
Full code below:
// MARK: Views
struct ContentView: View {
@State private var day = Day()
var body: some View {
MealView(meal: $day.meals.first!)
}
}
struct MealView: View {
@Binding var meal: Meal
var body: some View {
NavigationStack {
List {
ForEach($meal.foods) { food in
NavigationLink(destination: FoodView(food: food)) {
Text(food.name)
}
}
}
}
}
}
struct FoodView: View {
@Binding var food: Food
var body: some View {
VStack {
TextField("Food", text: $food.name)
}
}
}
// MARK: Data
class Day {
var meals: [Meal] = [Meal()]
}
@Observable
class Meal: Identifiable {
var id: UUID = UUID()
var foods: [Food] = [Food(name: "Pizza"), Food(name: "Pasta"), Food(name: "Steak")]
}
@Observable
class Food: Identifiable {
var id: UUID = UUID()
var name: String
init(name: String) {
self.name = name
}
}
@Binding
is only for values or structs not for classes that are @Observable
. Change it to let
for read only and @Bindable
if you want to get a binding from the class property, e.g.
struct MealView: View {
let meal: Meal
…
ForEach(meal.foods) { food in
NavigationLink(destination: FoodView(food: food)) {
Text(food.name)
}
}
…
struct FoodView: View {
@Bindable var food: Food
var body: some View {
VStack {
TextField("Food", text: $food.name)
}
}
}
Note usually Views are not named after model types.