4

Consider the following synthetic scenario:

import Combine

let publisher1 = PassthroughSubject<Int, Never>().eraseToAnyPublisher()
let publisher2 = PassthroughSubject<Int, Never>()


publisher1.sink { value in
    publisher2.send(value)
}

We have 2 publishers, I'd like to propagate any value of the publisher1 to the publisher2. The code I've shown does the job, but I'm interested whether there is a cleaner, declarative approach to this.

Note: both publisher1 and publisher2 are of the same type.

Details on the problem

publisher2 is a part of the API that is exposed by the "core" class. The "core" class has a "has a" relationship to a "child" class which in turn has a publisher1 as it's API.

Over the lifetime of the "core" class, the "child" class can be allocated and deallocated multiple times. This should be transparent to the subscribers of the "core" class which won't need to subscribe to the publisher2.

Code:

import UIKit
import Combine


class ChildClass {
    let publisher1 = PassthroughSubject<Int, Never>().eraseToAnyPublisher()
}

class CoreClass {
    let publisher2 = PassthroughSubject<Int, Never>()

    private var childClass: ChildClass?

    init() {
        allocateChildClass()
    }

    func allocateChildClass() {
        let child = ChildClass()
        childClass = child
        // Any way to simplify this?
        child.publisher1.sink { value in
            publisher2.send(value)
        }
    }

    func deallocateChildClass() {
        childClass = nil
    }
}


class Client {
    let core = CoreClass()

    init() {
        // Doesn't care about allocating or deallocating of the ChildClass
        core.publisher2.sink { value in
            print(value)
        }
    }
}

Trying to subscribe one publisher to another doesn't work:

publisher2
    .subscribe(publisher1)

No exact matches in call to instance method 'subscribe'

enter image description here

Richard Topchii
  • 7,075
  • 8
  • 48
  • 115
  • 1
    What is it that you are actually trying to achieve? Without more context, this seems like an [XY problem](https://xyproblem.info). Is `publisher2` or can you create another publisher that "shadows" `publisher1`? Does `publisher2` also emit values on its own? – Dávid Pásztor Jan 17 '22 at 10:41
  • 1
    Thanks for your comment, updated my questions with a bit more context on the problem. – Richard Topchii Jan 17 '22 at 10:47
  • A Subject can be a subscriber. There is no need for a sink and no need to call `send`. That sort of thing is totally contrary to the spirit of Combine. – matt Jan 17 '22 at 12:59
  • @matt please provide a code example. – Richard Topchii Jan 17 '22 at 14:12
  • Uh, `publisher1.subscribe(publisher2)`. – matt Jan 17 '22 at 14:16
  • Oh, also please do note that none of your code is "real" since it never says `.store`. None of the pipelines you have shown would actually work. – matt Jan 17 '22 at 14:18
  • What if `publisher1` is erased to any publisher? I've updated my question – Richard Topchii Jan 17 '22 at 14:27
  • Regarding the `store` - yes, true, but your idea is good. Is it possible to use `subscribe` or something similar, but with the "source" publisher type erased? – Richard Topchii Jan 17 '22 at 14:31
  • You said they were passthrough subjects. Why would you erase that? – matt Jan 17 '22 at 14:32
  • @matt I was able to the solution with your suggestion, I didn't notice the call order. I called `publisher1.subscribe(publisher2)` and everything worked well, thank you! Feel free to post your solution as an answer and I'll accept it. – Richard Topchii Jan 17 '22 at 14:35
  • I would just be repeating answers I've given before. See for example https://stackoverflow.com/questions/65386645/how-to-compose-a-combine-publisher-that-emits-the-last-emitted-value-to-its-2nd. I'm tempted to suggest your question is a duplicate. – matt Jan 17 '22 at 14:49
  • Also I've explained all this for you at https://www.apeth.com/UnderstandingCombine/publishers/publisherssubject.html – matt Jan 17 '22 at 14:49
  • 1
    Thanks, great materials! I think it's still worth adding the answer, some people might search for this – Richard Topchii Jan 17 '22 at 15:10
  • 3
    Well, okay, findability is important. – matt Jan 17 '22 at 15:18

1 Answers1

7

It is a delightful feature of PassthroughSubject that it is both a publisher and an operator: a Subject can be chained directly to a pipeline.

So just say

publisher1.subscribe(publisher2)

and you're all set.

matt
  • 515,959
  • 87
  • 875
  • 1,141