I’m creating a MacOS app and wanted to store the app state in memory and pass it around to a few components, but also update the state from those components.
import SwiftUI
@Observable
class Book {
var title: String
init(_ title: String) {
self.title = title
}
}
struct ParentView : View {
@State private var myBook = Book("First title")
var body: some View {
VStack {
Text("Book title (Parent): \(myBook.title)")
ChildView()
.frame(width: 200, height: 100)
.background(.teal)
.environment(myBook) // Add myBook to environment
}
}
}
struct ChildView : View {
@Environment(Book.self) var myBook: Book
var body: some View {
VStack {
Text("Book title (Child): \(myBook.title)")
TextField("Book title", text: myBook.title) // Error here!!!
}
}
}
This throws an error when I attempt to pass myBook.title as a Binding to the TextField.
Cannot convert value of type 'String' to expected argument type 'Binding<String>'
This makes sense but normally, but since the Book is an @Observable class I can update the values and have the updates reflected across all usages.
What’s the best way to assign an @Observable @Environment class property to a Binding parameter?
>Solution :
To make it a Binding, you need to use Bindable:
struct ChildView : View {
@Environment(Book.self) var myBook: Book
var body: some View {
VStack {
Text("Book title (Child): \(myBook.title)")
@Bindable var myBook = myBook
TextField("Book title", text: $myBook.title)
}
}
}
Documentation (which includes a similar example): https://developer.apple.com/documentation/swiftui/bindable
