23

Since Swift 5.5 we could create SwiftUI lists with bindings like this (e.g. see this answer):

class Item {
    // ...
    var isOn: Bool
}

struct ContentView: View {
    @State private var items: [Item] = []
    var body: some View {
        NavigationView {
            List {
                ForEach($items) { $item in     // <--- list binding
                    Toggle(isOn: $item.isOn) {
                        Text("Vibrate on Ring")
                    }
                }
            }
        }
    }
}

Now I want to do something similar with SwiftData but I get an error:

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]
    var body: some View {
        NavigationView {
            List {
                ForEach($items) { $item in //   <--- Cannot find '$items' in scope
    // ...

How can I render a list of SwiftData objects that allows inline editing of objects through bindings?

1
  • You have to use Bindable with the new frameworks. Commented Jun 13, 2023 at 12:23

3 Answers 3

26

You can utilize the new Bindable, which has a subscript operator to wrap the underlying data into a binding variable:

ForEach(items) { item in
    Toggle(isOn: Bindable(item).isOn) { ... }
}

If the binding need to be reused multiple times, you can also create the Bindable object within the view:

ForEach(items) { item in
    @Bindable var item = item
    Toggle(isOn: $item.isOn) { ... }
}

Note the usage of $, since the bindable object is created as a property wrapper.

Sign up to request clarification or add additional context in comments.

3 Comments

Correct, it's even in the docs although its on 2 lines instead of 1.
Hi, malhal. Do you have a link to that example in the documentation?
@MikikoJane Have added an example of using it in 2 separate lines. Similar example can be found at Bindable
11

The easiest solution I found is to use @Bindable and to separate the Query macro and the Bindable into different views.

An example

struct ItemRow: View {
    @Bindable var item: Item
   
    var body: some View {
        HStack {
            Text(item.name)
            Toggle(isOn: $item.isOn) // Notice the $
        }
    }
}

Calling view

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    ItemRow(item: item)
     //...
}

5 Comments

This is beta, requires SwiftData and the new OS versions coming fall 2023.
Yes of course, the whole question is about beta versions so what is your point?
It's just to clarify, as this may not be obvious to everyone.
Ok but why not clarify that for the whole question then, that’s what made me react.
You're right, that's true...
0

Swift Bindable Structure can be used like the example given below.

import SwiftUI
import SwiftData

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query private var items: [Item]
    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    Toggle(isOn: Bindable(item).isOn) {
                        Text("Vibrate on Ring")
                    }
                }
            }
        }
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.