Search code examples
swiftcombine

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?


Solution

  • 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)