Search code examples
iosswiftxcodeswiftuicombine

SwiftUI run method on view when Published view model member value changes


I am having troubles with running view methods on published property value change. My playground sample code looks like this:

class MyFoo: ObservableObject {
    @Published var bar: String
    init(bar: String) {
        self.bar = bar
    }

    func setNewText(newString: String) {
        self.bar = newString
    }

    func runFunctions() {
        setNewText(newString: "Test")
    }
}

struct TestView: View {
    @ObservedObject let foo = MyFoo(bar: "bar0")

    init(){
        let barSink = foo.$bar
            .sink() { //THIS IS WHERE I GET ERROR "Escaping closure captures mutating 'self' parameter"
                self.printResult(result: $0)
            }
    }

    func printResult(result: String) {
        print(result)
    }
}

let a = TestView()

Basically I know why I get this error in closure but I don't know how to go around it. Is this the right approach for running view methods on VM published property value changed?

I need this because I am using custom spinner that is not SwiftUI ready so I cant bind to it, and the only way to show/hide it is by calling its methods.

Any help would be most appreciated


Solution

  • I don't know how the playground works but I try to answer you question. I tested the code in the simulator and on a real device:

    
    class MyFoo: ObservableObject {
        @Published var bar: String
        init(bar: String) {
            self.bar = bar
        }
    
        func setNewText(newString: String) {
            self.bar = newString
        }
    
        func runFunctions() {
            setNewText(newString: "Test")
        }
    
    
    }
    
    struct TestView: View {
        @ObservedObject var foo = MyFoo(bar: "bar0")
    
        var body: some View {
            Text("Lolz")
                .onReceive(self.foo.$bar, perform: { lolz in
                    self.printResult(result: lolz)
                })
        }
    
        func printResult(result: String) {
            print(result)
        }
    
    }
    
    struct ContentView: View {
        var body: some View {
            TestView()
        }
    }
    

    The @ObservedObject needs to be a var. With onRecieve() you can listen to the publisher and update UI or call a function.

    I hope this helps.