UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Published<String> To Binding<String> ?

Forums > SwiftUI

Hi All,

I am creating a simple app that manages User detail, but seeing this: Cannot convert value of type 'Published<[String]>.Publisher' to expected argument type 'Binding<String>'

I am reasonable new to Swift/SwiftUI after coming across from ObjecticeC. It's a pretty steep learning curve :-)

I hope the following will show you what I am trying to do.

The code below is from a seperate .Swift file:

class Users: ObservableObject {
    @Published var firstname = [String]()
    @Published var lastname = [String]()
    @Published var email = [String]()
    @Published var password = [String]()
    @Published var location = [String]()
    @Published var mobile = [String]()
    @Published var service = [String]()

    init() {
        self.firstname = firstname
        self.lastname = lastname
        self.email = email
        self.password = password
        self.location = location
        self.mobile = mobile
        self.service = service
    }

I am not sure if this is the correct format, but please comment as needed.

This is how I'm declaring in the View:

@EnvironmentObject var user: Users

And this is how I'm trying to use it. InputField is a custom TextField.

InputField(text: user.$firstname, title: "First Name", place_holder: "").foregroundColor(.white).padding(.leading, 50)

Now at this point, I see the error message. How do I get around this?

Fritzables

   

Hi @Fritzablez A few pointers Not sure of InputField as you have not shown this but I am assuming that text is a Binding var text: String in the struct which is passed to a TextField however you are passing in an array! The firstname is an array and should be a value eg @Published var firstname = "" PS genaral use camel case in Swift firstName

I know that you have come from ObjecticeC(🤯) but like any langauge start from the begining. Look at excellent course of 100 days of SwiftUI as Swift/SwiftUI are different from ObjecticeC. Plus you will see how to use the new @Observable macro.

   

Hey NigelGee,

This is how I have created the InputField.

struct InputField: View {

    @Binding var text: String
    let title: String
    let place_holder: String
    var isSecureField = false

    var body: some View {

        if isSecureField {

            VStack {
                HStack {
                    Text(title)
                        .fontWeight(.semibold)
                        .font(.system(size: 14))
                    Spacer()
                }
            }
            HStack {
                SecureField(place_holder, text: $text)
                    .frame(width: UIScreen.main.bounds.width - 100, height: 30)
                    .font(.system(size: 18, weight: .semibold))
                    .foregroundColor(.black)
                    .background(Color(.white))
                Spacer()
            }
            Divider()

        } else {
            VStack {
                HStack {
                    Text(title)
                        .fontWeight(.semibold)
                        .font(.system(size: 14))
                    Spacer()
                }
            }
            HStack {
                TextField(place_holder, text: $text)
                    .frame(width: UIScreen.main.bounds.width - 100, height: 30)
                    .font(.system(size: 18, weight: .semibold))
                    .foregroundColor(.black)
                    .background(Color(.white))
                    .textInputAutocapitalization(.never)
                Spacer()
            }
            Divider()
        }
    }
}

#Preview {
    InputField(text: .constant(""), title: "Email", place_holder: "Sample")
}

Not sure if this shines light on it.

From what you have said, I should be seperating out each Property (ie: FirstName, LastName,...) and @Publish them seperately??

Pete

PS: Yea, I only found this site last night and I was allready in the deep-end. :-)

   

OK thought that the case this is rough so you can get an idea Change the InputField to (this will give default for field that are not used in call.

struct InputField: View {

    @Binding var text: String
    let title: String
    let placeholder: String
    var isSecureField: Bool

    init(text: Binding<String>, title: String, placeholder: String = "", isSecureField: Bool = false) {
        _text = Binding(projectedValue: text)
        self.title = title
        self.placeholder = placeholder
        self.isSecureField = isSecureField
    }

    // Rest of code

The User model

struct User: Identifiable {
    var id = UUID()
    var firstName: String
    var lastName: String
    var email: String
    var password: String
    var location: String
    var mobile: String
    var service: String
}

The Users class

@Observable
class Users {
    var array = [User]()
}

Remember to put the Users into envrioment this usually done in the App Window Group @main struct TestApp: App { @State private var users = Users()

var body: some Scene {
    WindowGroup {
        ContentView()
            .environment(users)
    }
}

} Now in ContentView can do this

struct ContentView: View {
    @Environment(Users.self) var users
    @State private var user = User(firstName: "", lastName: "", email: "", password: "", location: "", mobile: "", service: "")

    var body: some View {
        VStack {
            InputField(text: $user.firstName, title: "First Name")
            InputField(text: $user.lastName, title: "Last Name")
            InputField(text: $user.password, title: "Password", isSecureField: true)
            // …… etc

            Button("Submit") {
                let newUser = User(firstName: user.firstName, lastName: user.lastName, email: user.email, password: user.password, location: user.location, mobile: user.mobile, service: user.service)
                users.array.append(newUser)
                user = User(firstName: "", lastName: "", email: "", password: "", location: "", mobile: "", service: "")
            }
            .buttonStyle(.bordered)

            List(users.array) { user in
                Text(user.firstName)
            }

            Spacer()
        }
        .padding()
    }
}

#Preview {
    ContentView()
        .environment(Users())
}

If you run in a Test/Sandbox project and have a play with it there. Hope that some help.

   

Nice work NigelGee,

Thats awsome. should keep me out of trouble for a while. Much appreciated Nigel.

Pete

   

I have been thinking about the InputField and changed it a bit

struct InputField: View {
    @Binding var text: String
    let title: LocalizedStringKey
    let prompt: Text?
    let axis: Axis
    var isSecureField: Bool

    init(_ title: LocalizedStringKey, text: Binding<String>, prompt: Text? = nil, axis: Axis = .horizontal, isSecureField: Bool = false) {
        _text = text
        self.title = title
        self.prompt = prompt
        self.isSecureField = isSecureField
        self.axis = axis
    }

    var body: some View {
        VStack(alignment: .leading) {
            Text(title)
                .font(.headline)

            if isSecureField {
                SecureField("", text: $text, prompt: prompt)
                    .font(.headline)
                    .foregroundColor(.black)
                    .background(.white)
                    .accessibilityLabel(prompt ?? Text(title))
                    .autocorrectionDisabled()
                    .textInputAutocapitalization(.never)
            } else {
                TextField("", text: $text, prompt: prompt, axis: axis)
                    .font(.headline)
                    .foregroundColor(.black)
                    .background(.white)
                    .accessibilityLabel(prompt ?? Text(title))
            }
        }
    }
}

Which now mean you can use in Form

struct ContentView: View {
    @State private var username = ""
    @State private var password = ""
    @State private var note = ""

    var body: some View {
        Form {
            InputField("User Name", text: $username, prompt: Text("Required"))
            InputField("Password", text: $password, prompt: Text("Required"), isSecureField: true)
            InputField("Note", text: $note, axis: .vertical)
        }
    }
}

or in VStack

struct ContentView: View {
    @State private var username = ""
    @State private var password = ""
    @State private var note = ""

    var body: some View {
        VStack {
            InputField("User Name", text: $username, prompt: Text("Required"))
            Divider()
            InputField("Password", text: $password, prompt: Text("Required"), isSecureField: true)
            Divider()
            InputField("Note", text: $note, axis: .vertical)
            Divider()

            Spacer()
        }
        .padding(.horizontal)
    }
}

   

The error message you're encountering, "Cannot convert value of type 'Published<[String]>.Publisher' to expected argument type 'Binding<String>'", suggests that there's a type mismatch between the @Published property firstname, which is an array of strings, and the expected argument type for the InputField's text parameter, which seems to be a Binding<String>.

To resolve this, you need to ensure that InputField accepts an array of strings for the text parameter or adjust your Users class to handle individual strings rather than arrays. Since you're managing user details, it's more common to have individual strings for each user attribute like first name, last name, email, etc., rather than arrays.

Here's how you can adjust your Users class:

class Users: ObservableObject {
    @Published var firstname: String = ""
    @Published var lastname: String = ""
    @Published var https://moveinpak.com : String = ""
    @Published var password: String = ""
    @Published var location: String = ""
    @Published var mobile: String = ""
    @Published var service: String = ""
}

And then, adjust your usage of InputField like so:

InputField(text: $user.firstname, title: "First Name", place_holder: "").foregroundColor(.white).padding(.leading, 50)

By making these changes, you'll be able to bind individual string properties from your Users class to the text parameter of InputField, resolving the type mismatch error. Make sure you adjust other parts of your code accordingly to handle individual strings instead of arrays if necessary.

   

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.