NEW: Learn to build amazing SwiftUI apps for macOS with my new book! >>

Best practice for instance / row binding

Forums > SwiftUI

Please consider the following scenario broken down in this example:

struct Item: Hashable, Identifiable{
  var id = UUID()
  var text: String
  var someTrigger: Bool
}

class Items: ObservableObject {
  @Published var items = [Item]()
}

Then I have a List view, and a separate row view. The row view should represent one item (consider an item having more properties than just a text, this is only for simplification).

So I thought I could do something like this:

struct ContentView: View {
    @ObservedObject public var items = Items()

    var body: some View {
        NavigationView{
            List {
                ForEach(items.items, id: \.id) { item in

                  // option A
                  ItemRowView(text: item.text, someTrigger: self.$items.items[self.items.items.firstIndex(of: item)!].someTrigger)

                  // Option B
                  ItemRowView(item: item)                  
                }
             }
         }
    }
}

While option A works, if I have someTrigger as a binding like @Binding public var someTrigger. But if I have multiple properties and thus multiple bindings this gets muddy quite fast. So I think passing the item instance instead is the way to go (so option B). But from what I understood I then have to declare item as an @ObservedObject within ItemRowView. But item is a struct rather than a class. Does that mean I have to declare my items as classes? I don't want to start with ugly workarounds and thus am curious about the best practice approach in this situation.

Thanks for all your answers!

Best regards Leonard

1      

Okay so I came up with the following third option, which is close to Option B. I used the arrayItem as a binding itself (which works perfectly fine because the array items are structs):

  // Option C
  ItemRowView(item: $item)
struct ItemRowView: View {
  @Binding item: Item

  var body: some View {
    // all item.* properties are accessible and will change the item as expected 
    // ...
  }
}

1      

Hi @LennyN95 , I have the same issue but i don't understand how you solved it.

Can you please share the entire code of solution C?

Thanks, Matteo

1      

He made item itself bindable in the itemView and passed it in during the ForEach as $item.

I think there is a change in SwiftUI which allows you to be more explicit on this, in that you can set the binding in the ForEach statement. I remember seeing that in some video last week, either from WWDC or from Paul.

1      

Yes i did the same but i think the are something wrong in my code.

Because this code (that is pretty the same of @LennyN95)

LazyVStack(spacing: 0) {
    ForEach(viewModel.orders, id: \.id.string) { order in
        OrderView(order: $order)
    }
}

return the following error Cannot find '$order' in scope

The viewModel is defined as

@ObservedObject public var viewModel: OrdersViewModel

And inside the viewModel the array of orders is defined like

@Published public var orders = [Order]()

So, i don't understand what is wrong.

1      

What does your OrdersViewModel look like?

1      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Spend less time managing in-app purchase infrastructure so you can focus on building your app. RevenueCat gives everything you need to easily implement, manage, and analyze in-app purchases and subscriptions without managing servers or writing backend code.

Get Started

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.