.Sink Is Not Returning the Promise Values from a Future Publisher

.sink is not returning the promise values from a Future Publisher

What's going on is that your .sink is not followed by a .store command, so the pipeline goes out of existence before any value has a chance to come down it.

Your assignment of the pipeline to the empty _ effectively masks the problem. The compiler tried to warn you, and you shut it down.

Sink does not receive values when connecting to finished a Publisher

PassthroughSubject receives a value and passes it along, it doesn't store the value so if you subscribe to it after the value passed through it, you won't receive it.

You can use CurrentValueSubject, which will store the latest value and resend it whenever somebody subscribes to it.

All of this is moot if you send completion: .finished though. A completed publisher won't send any values to a subscriber because it's completed.

This is your fixed code:

func somePublisher() -> AnyPublisher<Bool, Never> {
let subject = CurrentValueSubject<Bool, Never>(true)
return subject.eraseToAnyPublisher()
}

var bag: Set<AnyCancellable> = []

somePublisher()
.first()
.sink { _ in
print("Completed")
} receiveValue: {
print("Received \($0)")
}
.store(in: &bag)

Future Combine sink does not recieve any values

Try to change/remove .cancel() method on your subscriptions. Seems you subscribe to the publisher, and then immediately cancel the subscription. The better option is to retain and store all your subscriptions in the cancellable set.

Publisher sink never runs completion

It turned out that the issue was that the controller was deinitialized early and therefore the promise could not respond to the observer. So it had nothing to do with Combine or the code I posted, but with the way I initialized the controller.

Why can't I get passthrough subjects to work for combine?

The warning that you are seeing that the subscription is unused is a hint to store the token returned by sink like so:

let mySubject = PassthroughSubject<String, Error>()

let cancellable = mySubject
.sink(
receiveCompletion: { completion in
print("-- completion", completion)
},
receiveValue: { value in
print("-- value", value)
}
)

then when you call:

mySubject.send("one")
mySubject.send("two")
mySubject.send("three")

you will see this printed out:

-- value one
-- value two
-- value three

you can cancel the subscription if you are not longer interested in receiving updates:

cancellable.cancel()

or you can send a completion:

mySubject.send(completion: .finished)

and then you will see this printed out:

-- completion finished

Combine's DataTaskPublisher does not output when passed through flatMap in Playgrounds

Swift playgrounds finish execution when all synchronous code in them returned. However, you are executing a network request asynchronously, so you need to tell the playground to wait for the async result.

Call this before starting the network request:

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

And in the sink, you can finish execution by calling

PlaygroundPage.current.finishExecution()

Combine sink: ignore receiveValue, only completion is needed

CurrentValueSubject seems a confusing choice, because that will send an initial value (of Void) when you first subscribe to it.

You could make things less ambiguous by using Future, which will send one-and-only-one value, when it's done.

To get around having to receive values you don't care about, you can flip the situation round and use an output type of Result<Void, Error> and a failure type of Never. When processing your network request, you can then fulfil the promise with .failure(error) or .success(()), and deal with it in sink:

let pub = Future<Result<Void, Error>, Never> {
promise in
// Do something asynchronous
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
promise(.success(.success(())))
//or
//promise(.success(.failure(error)))
}
}.eraseToAnyPublisher()

// somewhere else...
pub.sink {
switch $0 {
case .failure(let error):
print("Whoops \(error)")
case .success:
print("Yay")
}
}

You're swapping ugly code at one end of the chain for ugly code at the other, but if that's hidden away behind AnyPublisher and you're concerned with correct usage, that seems the way to go. Consumers can see exactly what to expect from looking at the output and error types, and don't have to deal with them in separate closures.



Related Topics



Leave a reply



Submit