Reputation: 1454
I am currently learning SwiftUI and trying to understand @EnvironmentObject
, which I find a bit confusing.
I want to share a store containing some data throughout my app, so I wrote this:
class Store: ObservableObject {
@Published var user: User?
}
class User: ObservableObject {
@Published var name: String?
}
My app view:
struct AppView: View {
@EnvironmentObject var store: Store
var body: some View {
ContentView()
.environmentObject(store)
}
}
}
SceneDelegate:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var store = Store()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let appView = AppView().environmentObject(store)
window.rootViewController = UIHostingController(rootView: appView)
self.window = window
window.makeKeyAndVisible()
}
}
}
And in ContentView
:
struct ContentView: View {
@EnvironmentObject var store: Store
var body: some View {
VStack {
TextField("Type some text here", text: $store.user?.name ?? "")
}
}
}
However, I am getting an error on the line where I declare the TextField
:
Value of optional type 'Binding?' must be unwrapped to a value of type 'Binding'
I don't understand what this means, and don't know what to do here.
Thank you for your help
Upvotes: 0
Views: 2032
Reputation: 54426
Here is a possible solution for the case if both user
and name
variables can be nil
- you need to use a custom binding:
struct ContentView: View {
@EnvironmentObject var store: Store
var binding: Binding<String> {
.init(get: {
store.user?.name ?? ""
}, set: {
store.user?.name = $0
})
}
var body: some View {
VStack {
TextField("Type some text here", text: binding)
}
}
}
But you should rather consider displaying this TextField
only when the user
variable is present. Otherwise what are you really editing? It can't be the name of the user if the user is missing...
Alternatively, try making both these variables non-optional (and preferably changing User
to struct):
class Store: ObservableObject {
@Published var user = User(name: "test") // init here or in the `init`
}
struct User {
var name: String // assuming that a `User` always has a `name`
}
and the TextField
will work just fine:
TextField("Type some text here", text: $store.user.name)
Upvotes: 3