Another Dude
Another Dude

Reputation: 1454

SwiftUI: Value of optional type 'Binding<String>?' must be unwrapped to a value of type 'Binding<String>'

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

Answers (1)

pawello2222
pawello2222

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

Related Questions