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

564 Views Asked by At

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?

1

There are 1 best solutions below

0
Yogurt On BEST ANSWER

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)
   */