Binding a var from struct array under an ObservableObject

Binding a var from struct array under an ObservableObject
typescript
Ethan Jackson

I have an environment object Order, containing a list of OrderItem (aka an item + a quantity) I'm presenting a list with steppers so quantity can be modified for each item.

struct MenuItem: Codable, Hashable, Identifiable { var id: UUID var name: String var price: Int } struct OrderItem: Hashable, Identifiable { var item: MenuItem var quantity: Int var id: UUID } class Order: ObservableObject { @Published var items = [OrderItem]() } struct OrderView: View { @EnvironmentObject var order: Order var body: some View { List { Section("My Order") { ForEach($order.items) { $orderItem in VStack { Text(orderItem.item.name) HStack { Text("Quantity: \(orderItem.quantity)") Spacer() Stepper("", value: $orderItem.quantity) } } } } } } }

This works with simple binding. Nice... But I'd like to implement a custom binding here, so that user cannot decrement quantity below zero.

That's where getting issues.

I tried replacing my stepper binding this way :

Stepper("", value: bindingForItemQuantity($oi)) private func bindingForItemQuantity(_ bindedOrderItem: Binding<OrderItem>) -> Binding<Int> { let oi = bindedOrderItem.wrappedValue return Binding<Int>( get: { orderItem.quantity }, set: { newValue in if newValue >= 1 { orderItem.quantity = newValue } } ) }

But i'm getting errors Cannot assign to property: 'orderItem' is a 'let' constant

I guess because my OrderItem is a struct, so i'll update it to be a class. But then my view is not automaticaly updating when using the steppers. I've been hacking it so it ended working (toggling a State bool in my Binding.set) but I guess i'm just going the wrong way...

What would be a clean binding solution here ?

Answer

Update your code with

private func bindingForItemQuantity(_ bindedOrderItem: Binding<OrderItem>) -> Binding<Int> { return Binding<Int>( get: { bindedOrderItem.quantity.wrappedValue }, set: { newValue in print(newValue) if newValue >= 1 { bindedOrderItem.quantity.wrappedValue = newValue } } ) }

Related Articles