SwiftData list bindings with @Query – Swift

by
Ali Hasan
axios swift-concurrency swift-data swiftui-animation

Quick Fix: Utilize the Bindable to wrap the underlying data into a binding variable and use its subscript operator to access the isOn property. If the binding needs to be reused, create the Bindable object within the view using the property wrapper syntax with $.

The Solutions:

Solution 1: Using Bindable

Utilize the `Bindable` struct to wrap the underlying data into a binding variable:

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

Solution 2: Creating Bindable within View

If the binding needs to be reused multiple times, 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.

Solution 2: @Bindable and separating Query and Bindable

The easiest solution is to use both `@Bindable` and separate the `Query` macro and the `Bindable` into different views. This allows for a more modular and flexible approach.
Here’s an example:

ItemRow View

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

ContentView

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)
                }
            }
        }
    }
}

In this approach, the ItemRow view is responsible for displaying and editing a single item, while the ContentView is responsible for fetching and displaying a list of items. The @Bindable property wrapper ensures that any changes made to the item within the ItemRow view are propagated back to the ContentView.

Solution 3: Swift Bindable Structure

To render a list of SwiftData objects that allows inline editing of objects through bindings, you can use the Swift Bindable structure. Here’s an updated solution based on the provided code snippet:

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")
                    }
                }
            }
        }
    }
}

In this updated solution, we use the Bindable structure to wrap the Item object, which allows us to bind to its properties and edit them inline. The Bindable structure provides a way to create a SwiftUI-compatible binding from a SwiftData object, making it possible to use SwiftData objects in SwiftUI views.

Q&A

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

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

Is there a way we can separate the Query macro and the Bindable into different views?

Yes, you can use @Bindable and separate the Query macro and the Bindable into different views.

Can we use Swift Bindable Structure?

Yes, you can use Swift Bindable Structure.

Video Explanation:

The following video, titled "SwiftData list bindings with @Query - YouTube", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

SwiftData list bindings with @Query I hope you found a solution that worked for you 🙂 The Content (except music & images) is licensed under ...