Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.send() and .sink() do not seem to work anymore for PassthroughSubject in Xcode 11 Beta 5

In the following code "Test" should be printed in the console when the Button is pressed, but it's not. The event is not send through the publisher. Any idea what happened with PassthroughSubject in Xcode 11 Beta 5 ? (in Xcode 11 Beta 4 it works well)

var body: some View {  

    let publisher = PassthroughSubject<String, Never>()

    publisher.sink { (str) in  
        print(str)  
    }  
    return Button("OK") {  
        publisher.send("Test")  
    }  
}

P.S. I know there are other ways to print a string when a button is pressed, I just wanna show a simple send-receive example

like image 533
Sorin Lica Avatar asked Jul 30 '19 11:07

Sorin Lica


2 Answers

.sink() returns an AnyCancellable object. You should never ignored it. Never do this:

// never do this!
publisher.sink { ... }
// never do this!
let _ = publisher.sink { ... }

And if you assign it to a variable, make sure it is not short lived. As soon as the cancellable object gets deallocated, the subscription will get cancelled too.

// if cancellable is deallocated, the subscription will get cancelled
let cancellable = publisher.sink { ... }

Since you asked to use sink inside a view, I'll post a way of doing it. However, inside a view, you should probably use .onReceive() instead. It is way more simple.

Using sink:

When using it inside a view, you need to use a @State variable, to make sure it survives after the view body was generated.

The DispatchQueue.main.async is required, to avoid the state being modified while the view updates. You would get a runtime error if you didn't.

struct ContentView: View {
    @State var cancellable: AnyCancellable? = nil

    var body: some View {
        let publisher = PassthroughSubject<String, Never>()

        DispatchQueue.main.async {
            self.cancellable = publisher.sink { (str) in
                print(str)
            }
        }

        return Button("OK") {
            publisher.send("Test")
        }
    }
}

Using .onReceive()

struct ContentView: View {

    var body: some View {
        let publisher = PassthroughSubject<String, Never>()

        return Button("OK") {
            publisher.send("Test")
        }
        .onReceive(publisher) { str in
            print(str)
        }
    }
}
like image 106
kontiki Avatar answered Oct 12 '22 20:10

kontiki


You are missing .store when you subscribe to the sink. You can use .onReceive, but your code is not receiving values because you need to add .store(in: &subscription)

var body: some View {  
    var subscription = Set<AnyCancellable>()
    let publisher = PassthroughSubject<String, Never>()

    publisher.sink { (str) in  
        print(str)  
    }.store(in: &subscription)

    return Button("OK") {  
        publisher.send("Test")  
    }  
}
like image 35
zdravko zdravkin Avatar answered Oct 12 '22 22:10

zdravko zdravkin