'SwiftUI isEmpty on StateObject variable always returns true

I'm trying to get a form validation that checks if the value of a TextField isEmpty to disable the save button on that form. However it seems the check for "isEmpty" always returns true even when a value is entered in the TextField. So the button always stays disabled.

This is what my form looks like:

struct AddPHVTestView: View {
    
    @StateObject private var addPHVTestVM = AddPHVTestViewModel()
    @Environment(\.presentationMode) var presentationMode
    
    let player: PlayerViewModel
    
    /*private var formIsValid : Bool {
        return (addPHVTestVM.standingHeight.isEmpty /* || addPHVTestVM.sittingHeight = "" || addPHVTestVM.chairHeight = "" || addPHVTestVM.weight = ""*/)
    }*/
    
    private var disableForm: Bool {
        addPHVTestVM.standingHeight.isEmpty
    }
    
    var body: some View {
        
        VStack(alignment: .leading) {
            Text("Test für \(player.firstName)")
                .font(.title)
                .padding(.horizontal)
                .padding(.top)
            Form {
                FormDatePickerCellView(date: $addPHVTestVM.testdate, label: "Testdatum", image: "calendar")
                
                FormTextFieldNumberCellView(text: $addPHVTestVM.standingHeight, label: "Größe im Stand", image: "figure.wave", placeholder: "in cm")
                    .keyboardType(.decimalPad)
                
                HStack {
                    Spacer()
                    
                    Button {
                        print(disableForm)
                        //addPHVTestVM.addPHVTestForPlayer(vm: player)
                        presentationMode.wrappedValue.dismiss()
                    } label: {
                        Text("Speichern")
                    }
                    .disabled(disableForm)

                    Spacer()
                }
            }
            .padding(.top, -20)
            .onChange(of: addPHVTestVM.standingHeight) { newValue in
                //print(formIsValid)
            }
        }
    }
}

This is the viewModel used:

class AddPHVTestViewModel: ObservableObject {
    
    @Published var testdate: Date = Date()
    @Published var standingHeight: String = ""
    @Published var sittingHeight: String = ""
    @Published var weight: String = ""
    @Published var chairHeight: String = ""
    
    func addPHVTestForPlayer(vm: PlayerViewModel) {
        
        let player: Player? = Player.byId(id: vm.id)
        if let player = player {
            let phvTest = PHVTest(context: PHVTest.viewContext)
            phvTest.testDate = testdate
            phvTest.standingHeight = Double(standingHeight) ?? 0.0
            phvTest.sittingHeight = Double(sittingHeight) ?? 0.0
            phvTest.weight = Double(weight) ?? 0.0
            phvTest.chairHeight = Double(chairHeight) ?? 0.0
            phvTest.player = player
            
            phvTest.save()
        }
        
    }
    
}

And here is the FormTextFieldNumberCellView:

struct FormTextFieldNumberCellView: View {
    @Binding var text: String
    let label: String
    let image: String
    let placeholder: String
    
    let formatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        return formatter
    }()
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Label("\(label)", systemImage: "\(image)")
                .font(.footnote)
            
            TextField("\(placeholder)", value: $text, formatter: formatter)
                .frame(maxWidth: .infinity)
                .padding(.top, 5)
        }
    }
}


Solution 1:[1]

The issue here is your standingHeight var being of type String. Change that implementation to be an Int and you are good to go.

@Published var standingHeight: Int = 0

and in your View:

.disabled(addPHVTestVM.standingHeight == 0)

struct FormTextFieldNumberCellView: View {
    @Binding var text: Int
    let label: String
    let image: String
    let placeholder: String
    
    let formatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        return formatter
    }()
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Label("\(label)", systemImage: "\(image)")
                .font(.footnote)
            
            TextField("\(placeholder)", value: $text, formatter: formatter)
                .keyboardType(.decimalPad)
                .frame(maxWidth: .infinity)
                .padding(.top, 5)
        }
    }
}

Edit:

The combination of NumberFormatter and the String var is the reason this is not working. The NumberFormatter tries to put a number in a String var. This should throw an error but it seems it dies gracefully. Your standingHeight var allways stays "", so .isEmpty is allways true and it never changes.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1