Skip to content

Instantly share code, notes, and snippets.

@grafele
Last active December 27, 2023 01:27
Show Gist options
  • Save grafele/227c7c04d04837fb0a91d97a25f22424 to your computer and use it in GitHub Desktop.
Save grafele/227c7c04d04837fb0a91d97a25f22424 to your computer and use it in GitHub Desktop.
Easiest way of creating a Combine-Publisher using a closure. Equivalent to Observable.create from RxSwift
extension Publishers {
public struct Anonymous<Output, Failure: Swift.Error>: Publisher {
private var closure: (AnySubscriber<Output, Failure>) -> Void
public init(closure: @escaping (AnySubscriber<Output, Failure>) -> Void) {
self.closure = closure
}
public func receive<S>(subscriber: S) where S : Subscriber, Anonymous.Failure == S.Failure, Anonymous.Output == S.Input {
let subscription = Subscriptions.Anonymous(subscriber: subscriber)
subscriber.receive(subscription: subscription)
subscription.start(closure)
}
}
}
extension Subscriptions {
final class Anonymous<SubscriberType: Subscriber, Output, Failure>: Subscription where SubscriberType.Input == Output, Failure == SubscriberType.Failure {
private var subscriber: SubscriberType?
init(subscriber: SubscriberType) {
self.subscriber = subscriber
}
func start(_ closure: @escaping (AnySubscriber<Output, Failure>) -> Void) {
if let subscriber = subscriber {
closure(AnySubscriber(subscriber))
}
}
func request(_ demand: Subscribers.Demand) {
// Ignore demand for now
}
func cancel() {
self.subscriber = nil
}
}
}
extension AnyPublisher {
static func create(_ closure: @escaping (AnySubscriber<Output, Failure>) -> Void) -> AnyPublisher<Output, Failure> {
return Publishers.Anonymous<Output, Failure>(closure: closure)
.eraseToAnyPublisher()
}
}
Copy link

ghost commented Mar 12, 2022

Was searching for a way to start a Combine pipeline from a closure and found your snippet.

After playing around with it, I wondered if there was no easier way to accomplish this, esp. I didn't want to have to manage the subscriber in the closure with which the publisher was created. So I came up with the following.

Usage site looks like this: Just.go { /* do some stuff and return a value */ }.

What do you think?

import Combine

extension Just where Output == Void {
    static func go<T>(_ closure: () throws -> T) -> AnyPublisher<T, Swift.Error> {
        Just(())
            .tryMap { _ -> T in
                return try closure()
            }
            .eraseToAnyPublisher()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment