msmialko
msmialko

Reputation: 1619

Sink does not receive values when connecting to finished a Publisher

I'm struggling to understand why no values are received to sink in the underlying code.

func somePublisher() -> AnyPublisher<Bool, Never> {
   let subject = PassthroughSubject<Bool, Never>()
   subject.send(true)
   subject.send(completion: .finished)
   return subject.eraseToAnyPublisher()
}

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

Output:

Completed

It looks like values are not received by the publishers down the stream if it was finished before it was connected. Is that right? How could I fix that if my publisher can finish synchronously?

Upvotes: 4

Views: 3601

Answers (2)

Oliver Pearmain
Oliver Pearmain

Reputation: 20600

I was struggling with this same problem and I made use of RunLoop.current to overcome it...

func somePublisher() -> AnyPublisher<Bool, Never> {
   let subject = PassthroughSubject<Bool, Never>()
   RunLoop.current.perform {
       // Delay send/completion until next run loop to give subscribers a chance to subscribe
       subject.send(true)
       subject.send(completion: .finished)
   }
   return subject.eraseToAnyPublisher()
}

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

Upvotes: 0

EmilioPelaez
EmilioPelaez

Reputation: 19912

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)

Upvotes: 5

Related Questions