UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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

2      

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 
    // ...
  }
}

2      

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

2      

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.

2      

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.

2      

What does your OrdersViewModel look like?

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.