Subject's subscription cancelled before values are sent downstream

I've got a publisher that subscribes to a PassthroughSubject and forwards the subject's values downstream. However, when I subscribe to the publisher, I see that the subscription is cancelled before I have a chance to push values through via the subject.

I've distilled the issue into the following minimal test case.

import XCTest
import Combine

@available(OSX 10.15, *)
public struct PassthroughPublisher: Publisher {
  public typealias Output = String
  public typealias Failure = Error
  let subject: PassthroughSubject<String, Error>

  init(subject: PassthroughSubject<String, Error>) {
    self.subject = subject
  public func receive<S>(subscriber: S)
      where S : Subscriber, PassthroughPublisher.Failure == S.Failure, PassthroughPublisher.Output == S.Input

class MinimalTestCase: XCTestCase {

  func testPassthroughPublisher() {
    let promise = expectation(description: "Call completes successfully")
    let subject = PassthroughSubject<String, Error>()
    let publisher = PassthroughPublisher(subject: subject)
    _ = publisher
        receiveCompletion: { completion in
          switch completion {
          case .finished:
          case .failure(let error):
            XCTFail("Unnexpected error " + error.localizedDescription)
        receiveValue: { value in
          XCTAssert(value == "a")
    wait(for: [promise], timeout: 5)

When I run the test, I see that the subscription is cancelled before values are sent via the subject.

Test Suite 'MinimalTestCase' started at 2019-07-21 09:47:43.534
Test Case '-[MinimalTestCase testPassthroughPublisher]' started.
receive subscription: (PassthroughSubject)
request unlimited
receive cancel
/[...]/Workspace.swift:49: error: -[MinimalTestCase testPassthroughPublisher] : Asynchronous wait failed: Exceeded timeout of 5 seconds, with unfulfilled expectations: "Call completes successfully".

What am I missing? Where am I going wrong?

I'm on Xcode version 11.0 beta 4 (11M374r).

You need to capture the Cancellable value returned from the sink, otherwise it will cancel the subscription when it’s deinited.


Aha! Thanks, that was it.

Terms of Service

Privacy Policy

Cookie Policy