Last active
June 20, 2022 08:19
-
-
Save danielt1263/a023a5bc7c583258e101a5024b6f8bc5 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// | |
// WithNextFrom.swift | |
// | |
// Created by Daniel Tartaglia on 7/07/21. | |
// Copyright © 2021 Daniel Tartaglia. MIT License. | |
// | |
import RxSwift | |
extension ObservableType { | |
/// Emit the next element from the second Observable immediatly after an element from the source Observable emits. The most recent from the source and the next event from the second observable will be passed to the resultSelector for processing. | |
/// - Parameters: | |
/// - second: The second observable to use. | |
/// - resultSelector: A pure function that accepts the latest source event and the next second observable emission. | |
/// - Returns: An observable that emits the result of the selector. | |
func withNextFrom<Source: ObservableConvertibleType, ResultType>(_ second: Source, resultSelector: @escaping (Element, Source.Element) throws -> ResultType) -> Observable<ResultType> { | |
let shared = share() | |
return Observable.merge( | |
shared.map(Either.right), | |
second.asObservable().map(Either.left).take(until: shared.takeLast(1)) | |
) | |
.scan((Element?.none, (Element, Source.Element)?.none)) { current, latest in | |
switch latest { | |
case let .left(value): | |
return current.0.map { (nil, ($0, value)) } ?? (nil, nil) | |
case let .right(value): | |
return (value, nil) | |
} | |
} | |
.compactMap { try $0.1.map { try resultSelector($0.0, $0.1) } } | |
} | |
/// Emit the next element from the second Observable immediatly after an element from the source Observable emits. | |
/// - Parameters: | |
/// - second: The second observable to use. | |
/// - Returns: An observable that emits the next value from `second` whenever the source emits. | |
func withNextFrom<Source: ObservableConvertibleType>(_ second: Source) -> Observable<Source.Element> { | |
let shared = share() | |
return Observable.merge( | |
shared.map(Either.right), | |
second.asObservable().map(Either.left).take(until: shared.takeLast(1)) | |
) | |
.scan((Element?.none, (Element, Source.Element)?.none)) { current, latest in | |
switch latest { | |
case let .left(value): | |
return current.0.map { (nil, ($0, value)) } ?? (nil, nil) | |
case let .right(value): | |
return (value, nil) | |
} | |
} | |
.compactMap { $0.1?.1 } | |
} | |
} | |
enum Either<A, B> { | |
case left(A) | |
case right(B) | |
} |
Yes. withLatestFrom
stores the last value emitted by the second observable and then emits it on demand from the source. This operator stores the last value emitted by the source, then emits it, along with the next value emitted by the second Observable. I've never needed it myself, but someone asked for it so I posted it here.
This saved me! Thanks!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is this in contrast to
withLatestFrom
which won't actually cause the second observable to emit an event if it hasn't done so already?