Skip to content

Instantly share code, notes, and snippets.

@swhitty
Last active September 17, 2021 23:43
Show Gist options
  • Save swhitty/1c12d4ecc948d25cef0e0eabfcc3eb53 to your computer and use it in GitHub Desktop.
Save swhitty/1c12d4ecc948d25cef0e0eabfcc3eb53 to your computer and use it in GitHub Desktop.
// Publisher type erasure that uses SE-0309 + unsafebitcast instead of the subclass approach of AnyPublisher.
// https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md
// Available in latest toolchains on swift.org
// OpenCombine.AnyPublisher
// https://github.com/OpenCombine/OpenCombine/blob/master/Sources/OpenCombine/AnyPublisher.swift
extension Publisher {
func eraseToAnyPub() -> AnyPub<Output, Failure> {
AnyPub(self)
}
}
struct AnyPub<Output, Failure: Error>: Publisher {
private let publisher: Publisher
init<P: Publisher>(_ publisher: P) where P.Output == Output, P.Failure == Failure {
self.publisher = publisher
}
func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input {
publisher.forceReceive(subscriber: subscriber)
}
}
struct AnySub<Input, Failure: Error>: Subscriber {
let combineIdentifier: CombineIdentifier
private let subscriber: Subscriber
init<S: Subscriber>(_ subscriber: S) where S.Input == Input, S.Failure == Failure {
self.subscriber = subscriber
self.combineIdentifier = subscriber.combineIdentifier
}
func receive(subscription: Subscription) {
subscriber.receive(subscription: subscription)
}
func receive(_ input: Input) -> Subscribers.Demand {
subscriber.forceReceive(input)
}
func receive(completion: Subscribers.Completion<Failure>) {
subscriber.forceReceive(completion: completion)
}
}
private extension Publisher {
func forceReceive<S: Subscriber>(subscriber: S) {
assert(Output.self == S.Input.self)
assert(Failure.self == S.Failure.self)
let anySub = unsafeBitCast(AnySub(subscriber), to: AnySub<Output, Failure>.self)
receive(subscriber: anySub)
}
}
private extension Subscriber {
func forceReceive<I>(_ input: I) -> Subscribers.Demand {
assert(Input.self == I.self)
return receive(unsafeBitCast(input, to: Input.self))
}
func forceReceive<F>(completion: Subscribers.Completion<F>) {
assert(Failure.self == F.self)
receive(completion: unsafeBitCast(completion, to: Subscribers.Completion<Failure>.self))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment