Search code examples
swifttwo-way-bindingcombine

Two-way binding in Swift Combine


I have a progress bar and a text field, both are updated depending on each other's input:

class ViewModel: ObservableObject {
    @Published var progressBarValue: Double {
        didSet {
            textFieldValue = String(progressBarValue)
        }
    }
    @Published var textFieldValue: String {
        didSet {
            progressBarValue = Double(progressBarValue)
        }
    }
}

Since updating one updates the other, I end up having an infinite recursion in my code.

Is there a way to workaround this with Combine or plain swift code?


Solution

  • Expanding on my comment, here is a minimal example of a slider and a textfield that both control (and be controlled by) a value via two-way bindings:

    class ViewModel: ObservableObject {
        @Published var progress: Double = 0
    }
    
    struct ContentView: View {
        @EnvironmentObject var model: ViewModel
    
        var body: some View {
            VStack {
                TextField("", value: self.$model.progress, formatter: NumberFormatter())
                Slider(value: self.$model.progress, in: 0...100)
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
                .environmentObject(ViewModel())
        }
    }
    

    Note that I also had to inject a ViewModel instance to my environment on AppDelegate in order for this to work (both on preview & actual app)