.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
(Cross-)Compiling Swift for Raspberry Pi
Structuring Data for Chat App in Firebase
Arkit Setting Worldtrackingconfiguration to Gravityandheading Produces Error
Swift Uitableviewautomaticdimension Is Not Working
Swiftui: Stop an Animation That Repeats Forever
Swift - Associated Value or Extension for an Enum
How to Add Nil Value to Swift Dictionary
Swift - Creating Shadow with 2 Different Colours for Imageview
Why Reloaddata Is Showing Error for My Tableview Inside Viewcontroller
How to Execute Different Implementation of a Method of a Generic Struct Based on the Generic Type
Check Whether the Arreferenceimage Is No Longer Visible in the Camera's View
Getting Pixel Format from Cgimage
How Assistant Model Works in Swift
Error: Couldn't Irgen Expression, No Additional Error
Swift Catch Enum Case with Binding
Swift How to Design Uiwebview Auto Resize Full Screen in Story Board