Last active
October 15, 2020 18:30
-
-
Save xcadaverx/26062e49d8d073291c26967d48599d8a to your computer and use it in GitHub Desktop.
[Combine.Publisher.Race] Custom Combine implementation of Race or amb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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