Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReactiveKit + Combine, combined? #251

Open
kevinrenskers opened this issue Jan 12, 2020 · 3 comments
Open

ReactiveKit + Combine, combined? #251

kevinrenskers opened this issue Jan 12, 2020 · 3 comments

Comments

@kevinrenskers
Copy link

I have a UIKit app, where I am starting to integrate a bit of SwiftUI into (via UIHostingController). My SwiftUI views need to use Combine (@State, or @ObservedObject+@Published for example), but I'd still like these views to work with my existing ReactiveKit Signals. Is this possible without writing a bunch of boilerplate code? Has anyone else tried to make ReactiveKit and Combine work together in one app?

@srdanrasic
Copy link
Contributor

Hey @kevinrenskers, check out the latest release. I've added toPublisher() extension method to SignalProtocol that converts ReactiveKit signal into Combine publisher, as well as toSignal() extension on Publisher that converts it the other way around.

@kevinrenskers
Copy link
Author

Very cool! 🎉

I love ReactiveKit's easy way to wrap old-fashioned async code with callbacks into Signals (https://github.com/DeclarativeHub/ReactiveKit#wrapping-asynchronous-calls-into-signals). I found it quite hard to do the same in pure Combine, so these new extensions will really help to go from async calls to Combine publishers. But, let's say I would want to skip the ReactiveKit in-between step at some point, would you suggest a similar route as the code in https://github.com/DeclarativeHub/ReactiveKit/blob/master/Sources/Combine.swift#L51? With the nested struct etc?

@srdanrasic
Copy link
Contributor

@kevinrenskers yeah, I miss that in Combine too. One approach could be to implement Signal in Combine, it should be pretty easy. Here is my quick attempt:

public struct Signal<Output, Failure: Error>: Combine.Publisher {

    private class Subscription: Combine.Subscription {

        let producer: (AnySubscriber<Output, Failure>) -> Combine.AnyCancellable
        let subscriber: AnySubscriber<Output, Failure>

        var cancellable: Combine.AnyCancellable?

        init(producer: @escaping (AnySubscriber<Output, Failure>) -> Combine.AnyCancellable, subscriber: AnySubscriber<Output, Failure>) {
            self.producer = producer
            self.subscriber = subscriber
        }

        func request(_ demand: Combine.Subscribers.Demand) {
            cancellable = producer(subscriber)
        }

        func cancel() {
            cancellable?.cancel()
        }
    }

    private let producer: (AnySubscriber<Output, Failure>) -> Combine.AnyCancellable

    public init(_ producer: @escaping (AnySubscriber<Output, Failure>) -> Combine.AnyCancellable) {
        self.producer = producer
    }

    public func receive<S>(subscriber: S) where S: Combine.Subscriber, Failure == S.Failure, Output == S.Input {
        let subscription = Subscription(producer: producer, subscriber: AnySubscriber(subscriber))
        subscriber.receive(subscription: subscription)
    }
}
let signal = Signal<Int, TestError> { subscriber in
    subscriber.receive(1)
    subscriber.receive(2)
    subscriber.receive(completion: .finished)
    return Combine.AnyCancellable {
        print("Cancelled")
    }
}

signal.sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants