Skip to content

Instantly share code, notes, and snippets.

@chockenberry
Created July 2, 2024 19:29
Show Gist options
  • Save chockenberry/d17914d40eb8e8e88285082db98c22c1 to your computer and use it in GitHub Desktop.
Save chockenberry/d17914d40eb8e8e88285082db98c22c1 to your computer and use it in GitHub Desktop.
NestedObservable
//
// ContentView.swift
// NestedObservables
//
// Created by Craig Hockenberry on 7/2/24.
//
import SwiftUI
@Observable
class Group: Identifiable {
init(name: String) {
self.name = name
self.options = []
}
let name: String
var options: [Option]
}
@Observable
class Option: Identifiable {
init(title: String) {
self.title = title
self.selected = false
}
let title: String
var selected: Bool
}
struct ContentView: View {
@State private var groups: [Group] = []
var body: some View {
Form {
ForEach(groups) { group in
Section {
ForEach(group.options) { option in
//Toggle(option.title, isOn: $option.selected) // Cannot find '$option' in scope
//@Bindable var selected = option.selected // Cannot reference invalid declaration 'option'
//Toggle(option.title, isOn: $selected)
let selectedBinding = Binding(get: { option.selected }, set: { option.selected = $0 })
Toggle(option.title, isOn: selectedBinding)
}
} header: {
Text(group.name)
}
}
Button("Check") {
for group in groups {
print("\(group.name):")
for option in group.options {
print(" \(option.title) = \(option.selected)")
}
}
}
}
.formStyle(.grouped)
.task {
for groupName in ["First", "Second", "Third"] {
let group = Group(name: groupName)
group.options.append(Option(title: "A"))
group.options.append(Option(title: "B"))
groups.append(group)
}
}
}
}
#Preview {
ContentView()
}
@chockenberry
Copy link
Author

The solution is to bind on the option (Observable), not the property:

@Bindable var option = option
Toggle(option.title, isOn: $option.selected)

Thanks to Helge Heß for the answer: https://mastodon.social/@helge/112718741179217009

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment