0

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?

Yogurt
  • 2,913
  • 2
  • 32
  • 63

1 Answers1

0

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)
   */
Yogurt
  • 2,913
  • 2
  • 32
  • 63