Skip to content

Instantly share code, notes, and snippets.

@xcadaverx
Last active October 15, 2020 18:30
Show Gist options
  • Save xcadaverx/26062e49d8d073291c26967d48599d8a to your computer and use it in GitHub Desktop.
Save xcadaverx/26062e49d8d073291c26967d48599d8a to your computer and use it in GitHub Desktop.
[Combine.Publisher.Race] Custom Combine implementation of Race or amb
import Foundation
import Combine
// MARK: - Either (Output)
enum Either<Left, Right> {
case left(Left)
case right(Right)
}
// MARK: - Race Publisher
extension Publishers {
struct Race<A: Publisher, B: Publisher>: Publisher where A.Failure == B.Failure {
typealias Output = Either<A.Output, B.Output>
typealias Failure = A.Failure
private let a: A
private let b: B
init(_ a: A, _ b: B) {
self.a = a
self.b = b
}
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = RaceSubscription(a, b, subscriber: subscriber)
subscriber.receive(subscription: subscription)
}
}
}
// MARK: - Race Subscription
extension Publishers.Race {
class RaceSubscription<A: Publisher, B: Publisher, S: Subscriber>: Subscription
where
Either<A.Output, B.Output> == S.Input,
A.Failure == S.Failure,
B.Failure == S.Failure
{
private var aCancellable: AnyCancellable?
private var bCancellable: AnyCancellable?
init(_ a: A, _ b: B, subscriber: S) {
aCancellable = a.sink(
receiveCompletion: { completion in
subscriber.receive(completion: completion)
self.bCancellable?.cancel()
},
receiveValue: { output in
_ = subscriber.receive(.left(output))
self.bCancellable?.cancel()
})
bCancellable = b.sink(
receiveCompletion: { completion in
subscriber.receive(completion: completion)
self.aCancellable?.cancel()
},
receiveValue: { output in
_ = subscriber.receive(.right(output))
self.aCancellable?.cancel()
})
}
func request(_ demand: Subscribers.Demand) {}
func cancel() {
aCancellable?.cancel()
bCancellable?.cancel()
}
}
}
// MARK: - Chaining Convenience
extension Publisher {
func race<P>(_ other: P) -> Publishers.Race<Self, P> where P : Publisher, Self.Failure == P.Failure {
return Publishers.Race(self, other)
}
}
// MARK: - Usage
let firstTimer = Timer.publish(every: 0.5, on: .main, in: .default)
.autoconnect()
let secondTimer = Timer.publish(every: 1.3, on: .main, in: .default)
.autoconnect()
let chainExampleCancellable = firstTimer.map { $0.description }
.race(secondTimer)
.sink { result in
switch result {
case .left(let string): print(string.count)
case .right(let date): print(date.advanced(by: 20))
}
}
let simpleExampleCancellable = Publishers.Race(firstTimer, secondTimer)
.sink { result in
switch result {
case .left(let leftValue): print("left value won!", leftValue)
case .right(let rightValue): print("right value won!", rightValue)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment