How to Bind or update class values with Button in SwiftUI

Viewed 22
//: [Previous](@previous)

import SwiftUI
import PlaygroundSupport


// class observer and update with a button. 
public class ItemClass2 {
    public var name: String
    let id: UUID
    public var number: Int = 0
    
    public init(name: String) {
        self.name = name
        self.id = .init()
    }
}

class ContentViewModel: ObservableObject {
    @Published var item = ItemClass2(name: "first")
}

struct ContentView: View {

    @StateObject var viewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Text("Item Struct")
                .font(.title)
            Text("Name: \(viewModel.item.name)")
                .font(.headline)
            Text("Number: \(viewModel.item.number)")
            TextField("name", text: $viewModel.item.name)
                .textFieldStyle(.roundedBorder)
            
            Button {
                viewModel.item.number += 1
//                viewModel
//                    .objectWillChange
//                    .send()
            } label: {
                Text("increase number")
            }
        }
        .frame(width: 400, height: 600)
    }
    
}

PlaygroundPage.current.setLiveView(ContentView())

If you put this in a playground, and tap the increment button, you'll notice that the number doesn't increment until the text field is modified.

I want to bind viewModel.item.number to the Button. If I change the property wrapper for the @StateObject var viewModel = ContentViewModel() to @Bindable it isn't allowed because viewModel is a class.

If we uncomment the objectWillChange lines, it solves the problem, but it feels sketchy and counter to the purpose of SwiftUI MVVM to have to manually call send().

How can I update the viewModel from a button tap, in a way that adheres to MVVM?

1 Answers

You must either:

  1. Make ItemClass2 a struct;
  2. Or keep calling objectWillChange.send().

This is happening because Combine doesn’t create a Binding relation with a refrence type(i.e class), so you must tell Combine when the value has changed or simply use a value type(i.e struct).

Related