Combine - Subscriber gets cancelled silently when subscribing a second time

1.2k Views Asked by At

I am trying to replace delegation pattern with Combine.

The architecture of my app is a VIPER, so I need to pass the sink subscriber from one module to another. The use case is i have data from module A (list) that needs to be show to module B (detail view), and module B can also update the data so I need to get it back to module A also.

With delegates it works okay but when I use a sink subscriber I am facing an issue.

The first time I go from module A to module B, I pass the subscriber and then subscribe it to the publisher (from moduleB) it works well and the subscriber in module A receives all the events from module B.

But then when dismissing the module B and routing again from A to B, then the subscriber receives immediately a cancel event when trying to subscribe again: receive subscription: (PublishedSubject) ... receive cancel ...

I made a very simplified example to show what is happening:

Module A:

class ViewController_A: UIViewController {
    
    var subscriber: AnySubscriber<String, Never>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        createSubscriber()
    }
    
    func createSubscriber() {
        let subscriber = Subscribers.Sink<String, Never>(
            receiveCompletion: { completion in
                print(completion)
            }, receiveValue: { value in
                print(value)
            })
        self.subscriber = AnySubscriber(subscriber)
    }
    
    func showViewControllerB() {
        let viewControllerB = ViewControllerB()
        viewControllerB.passSubscriber(AnySubscriber(subscriber))
    }
}

Module B:

protocol MyProtocol {
    var publisher: Published<String>.Publisher { get }
    func passSubscriber(_ subscriber: AnySubscriber<String, Never>)
}

class ViewController_B: UIViewController, MyProtocol {
    
    @Published var word: String = "House"
    var publisher: Published<String>.Publisher { $word }
    
    func passSubscriber(_ subscriber: AnySubscriber<String, Never>) {
        publisher
            .print()
            .subscribe(subscriber)
    }
    
    func dismiss() {
        dismiss(animated: true)
    }
}

When routing..

let viewControllerA = ViewController_A()
viewControllerA.showViewController_B() // When presenting B for the first time, receiving events here
// Dismiss B here...
viewControllerA.showViewControllerB() // When presenting B again (hence subscribing again), the subscription gets cancelled here without receiving any events/values

I noticed something interesting.. When I create the subscriber again every time I route to module B instead of creating it only one time in viewDidLoad, it seems to work okay, but I am not sure why.

Does this mean that a subscriber cannot subscribe to another publisher once it subscribed to one, even when the previous publisher doesn't exist anymore?

How I can make it work in my delegation case?

1

There are 1 best solutions below

3
On BEST ANSWER

When I create the subscriber again everytime I route to module B instead of creating it only one time in viewdidload, it seems to work okay

Correct, because that's just what you're supposed to do.

A subscriber that has been subscribed and then cancelled / completed is over, just as a publisher that has been subscribed to and then cancelled / completed is over. These are lightweight objects, created in order to facilitate communication between two "real" endpoints. When one of the endpoints comes to an end, the communication comes to an end.

So if you need a new pipeline of communication, you just make a new publisher / subscriber.