I'm new to Swift and SwiftUI and have a model with a @Published intValue optional and a view with a TextField where its text/string property is 2-way bound to intValue of the model. Need to make sure valid text entry as a string is converted to a valid integer for the model. What is the recommended approach to handle this in SwiftUI? Code is similar to below...
//Model
class AppModel:ObservedObject {
 @Published
 var intValue: Int? = 5
}
//View
@ObservedObject
 var appModel = AppModel.shared
TextField("", text: $appModel.intValue)
                    .keyboardType(.numberPad)
Updated sample code...
///////////
import SwiftUI
class Model: ObservableObject {
    static var shared: Model = Model()
    @Published
    var number: Int = 0
    init(){}
}
struct ContentView: View {
    var body: some View {
        TextFieldFormatter()
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
struct TextFieldFormatter: View {
    @ObservedObject
    var model: Model = Model.shared
    @State private var number: Int = 0
    let formatter: NumberFormatter = {
        let numFormatter = NumberFormatter()
        numFormatter.numberStyle = .none
        return numFormatter
    }()
    var body: some View {
        VStack {
            Text("State").onTapGesture {
                self.endEditing()
            }
            TextField("int", value: $number, formatter: formatter).keyboardType(.numberPad)
            Text("Echo: \(number)")
            Text("Observable")
            TextField("int", value: $model.number, formatter: formatter).keyboardType(.numberPad)
            Text("Echo: \(model.number)")
        }
    }
}
struct TextFieldFormatter_Previews: PreviewProvider {
    static var previews: some View {
        TextFieldFormatter()
    }
}
extension View {
    func endEditing() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
    }
}
                You can create a custom Binding to pass in to the TextField.
This is how to construct a Binding<String> (what the TextField needs):
var string = ""
let myBinding = Binding(get: { string }) {
    string = $0
}
The first argument of the Binding constructor is the getter. This closure should return a value of the desired type.
The second argument is a setter. It is a closure that takes in the new value, and does something with it (usually set another value).
So, if you want to create a Binding<String> to pass in to the TextField that gets/sets appModel.intValue, you can create a Binding<String> and pass it into the TextField like this:
TextField("", text: .init(
    get: { self.appModel.intValue.map(String.init) ?? "" },
    set: { self.appModel.intValue = Int($0) }
))
.keyboardType(.numberPad)
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With