reactivecombinepublishersubscriberreactive-swift

Reactive Swift Signal Producer conversion code to Combine.Publisher doesn't work with combine latest


Here's my code to convert ReactiveSwift Signal Producers to Combine.Publishers



import ReactiveSwift
import Combine

/// convert SignalProducer<X, Never> -> Publisher<X, Never>
public struct ReactiveSwiftPublisher<Element>: Publisher {
    public typealias Output = Element
    public typealias Failure = Never

    /// Subscription for ReactiveSwiftPublisher
    class Subscription<SubscriberType: Subscriber>: Combine.Subscription where SubscriberType.Input == Element {
        private var disposable: Disposable?

        init(producer: SignalProducer<Element, Failure>, subscriber: SubscriberType) {
            self.disposable = producer.startWithValues({
                _ = subscriber.receive($0)
            })
        }

        deinit {
            self.disposable?.dispose()
        }

        func request(_ demand: Subscribers.Demand) {}
        func cancel() {
            self.disposable?.dispose()
        }
    }

    private let producer: SignalProducer<Element, Failure>

    public init(producer: SignalProducer<Element, Failure>) {
        self.producer = producer
    }

    public func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
        let subscription = Subscription(producer: self.producer, subscriber: subscriber)
        subscriber.receive(subscription: subscription)
    }
}

extension SignalProducer where Error == Never {
    public var publisher: ReactiveSwiftPublisher<Value> {
        return ReactiveSwiftPublisher(producer: self)
    }
}

Creating a publisher is fine

let x = MutableProperty<Int>(0)
var cancellables = Set<AnyCancellable>()

x.producer.publisher.sink {
    print("$0")
}.store(in: &cancellables)

x.value = 33 // prints 33

but combining the latest doesn't yield a result

let x = MutableProperty<Int>(1)
let y = MutableProperty<Int>(0)
x.producer.publisher.combineLatest(y.producer.publisher).sink {
    print($0) // does not print
}.store(in: &self.cancellables)

but for some reason adding a current value subject makes it work

// add this above the previous block 
let subj = CurrentValueSubject<Int, Never>(0)

// and change the subscription to this
Publishers.CombineLatest3(x.producer.publisher, y.producer.publisher, self.subj.eraseToAnyPublisher()).sink {
    print($0) // this prints (1, 0, 0)
}.store(in: &self.cancellables)

Does anyone know what I'm doing wrong in my ReactiveSwiftPublisher code?


Solution

  • I changed my Subscription function to set up the producer's subscription when the request is called

        class Subscription<SubscriberType: Subscriber>: Combine.Subscription where SubscriberType.Input == Element {
            private var disposable: Disposable?
    
            private let subscriber: SubscriberType
            private let producer: SignalProducer<Element, Failure>
    
            init(producer: SignalProducer<Element, Failure>, subscriber: SubscriberType) {
                self.producer = producer
                self.subscriber = subscriber
            }
    
            deinit {
                self.disposable?.dispose()
            }
    
            func request(_ demand: Subscribers.Demand) {
                let subscriber = self.subscriber
                self.disposable = self.producer.startWithValues({
                    _ = subscriber.receive($0)
                })
            }
    
            func cancel() {
                self.disposable?.dispose()
            }
        }
    

    Here is a test

      let a = MutableProperty<Int>(0)
      let b = MutableProperty<Int>(1)
      var disposable: AnyCancellable?
    
      let aPub = a.producer.publisher
      let bPub = b.producer.publisher
    
      disposable = Publishers.CombineLatest(aPub, bPub).sink {
          print("complete: \($0)")
      } receiveValue: {
          print("value: \($0)")
      }
    
      a.swap(3)
      b.swap(3)
      
      /* 
       Prints:
       value: (0, 1)
       value: (3, 1)
       value: (3, 3)
       */