Why my published var is not being updated by my view?

Viewed 284

I am making a personal project to study SwiftUI. All was going well, the I noticed a bug on my app.

I have the simple view bellow, that saves a description, a value and some tags on my ViewModel. I am having an issue with the $viewModel.value. That variable is not being filled with values from the view.

I supose that my @Published var value: Double? from my ViewModel should be updated whenever the user types some value. Thing is, it is not updating on any iPhone 11 and up, but it works perfectly on the iPhone 8.

public struct AddBillView: View {
    
    @ObservedObject private var viewModel: AddBillViewModel
    
    @Environment(\.presentationMode) var presentationMode

    
    public let onExpenseCreated: ((_ expense: Expense)->Void)
    
    public var body: some View {
        Text("Add Expense")
        VStack {
            TextField("Descrição", text: $viewModel.name)
            HStack {
                Text("Valor \(NumberFormatter.currency.currencySymbol)")
                CurrencyTextField("Value", value: $viewModel.value)
                            .multilineTextAlignment(TextAlignment.leading)
            }
            
            HStack {
                Text("Tags")
                TextField("car pets home",
                          text: $viewModel.tags)
            }
                
            Picker("Type", selection: $viewModel.type) {
                Text("Paid").tag("Paid")
                Text("Unpaid").tag("Unpaid")
                Text("Credit").tag("Credit")
            }
        }.navigationTitle("+ Expense")
        
        Button("Adicionar") {
            if !viewModel.hasExpense() {
                return
            }
            onExpenseCreated(viewModel.expense())
            self.presentationMode.wrappedValue.dismiss()
        }
    }
    
    public init(viewModel outViewModel: AddBillViewModel,
                onExpenseCreated: @escaping ((_ expense: Expense)->Void)) {
        self.viewModel = outViewModel
        self.onExpenseCreated = onExpenseCreated
    }
}

And I have a ViewModel:

public class AddBillViewModel: ObservableObject {
    @Published var name: String = ""
    @Published var type: String = "Paid"
    @Published var tags: String = ""
    @Published var value: Double?
    
    
    init(expense: Expense?=nil) {
        self.name = expense?.name ?? ""
        self.type = expense?.type.rawValue ?? "Paid"
        self.tags = expense?.tags?.map { String($0.name) }.joined(separator: " ") ?? ""
        self.value = expense?.value
    }
    
    func hasExpense() -> Bool {
        if self.name.isEmpty ||
            self.value == nil ||
            self.value?.isZero == true {
            return false
        }
        return true
    }
    
    func expense() -> Expense {
        let tags = self.tags.split(separator: " ").map { Tag(name: String($0)) }
        return Expense(name: self.name, value: self.value ?? 0.0 ,
                       type: ExpenseType(rawValue: self.type)!,
                       id: UUID().uuidString,
                       tags: tags)
    }
}

Then I use my view:


                AddBillView(viewModel: AddBillViewModel()) { expense in
                    viewModel.add(expense: expense)
                    viewModel.state = .idle
                }

I already google it and spend a couple of hours looking for an answer, with no luck. Someone have any ideas?

Edited

Here is the code for the CurrencyTextField. I`m using this component: https://github.com/youjinp/SwiftUIKit/blob/master/Sources/SwiftUIKit/views/CurrencyTextField.swift

But the component works perfectly fine on iPhone 8 simulator and with a @State property inside my view. It does not work only with my ViewModel

1 Answers

I figured it out! The problem was that my AddBillViewModel is an ObservableObject and I was marking each property with @Published. This was causing some kind of double observable object.

I removed the @Published and it started working again.

Related