Skip to content

Commit 4a9e618

Browse files
committed
Added CurrentValuePublisher.flatMapLatest(_:)
1 parent 2654378 commit 4a9e618

2 files changed

Lines changed: 180 additions & 131 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import Combine
2+
3+
extension CurrentValuePublisher {
4+
5+
/// Produces a new `CurrentValuePublisher` by transforming the current value and all subsequent
6+
/// values into new `CurrentValuePublisher` instances, and republishing the values emitted
7+
/// by the most recently transformed inner publisher.
8+
///
9+
/// Use this operator to dynamically switch between publishers based on the current value
10+
/// while maintaining a `CurrentValuePublisher` interface.
11+
///
12+
/// - Parameter transform: A closure that transforms a value to a new `CurrentValuePublisher`.
13+
/// - Returns: A `CurrentValuePublisher` that emits the values from the most recently
14+
/// transformed inner publisher.
15+
///
16+
/// - SeeAlso: `Publisher.flatMapLatest(_:)`
17+
public func flatMapLatest<T>(
18+
_ transform: @escaping (Output) -> CurrentValuePublisher<T, Failure>
19+
) -> CurrentValuePublisher<T, Failure> {
20+
let currentInnerPublisher = transform(value)
21+
22+
switch (self.storage, currentInnerPublisher.storage) {
23+
case (.constant, .constant):
24+
// If the outer and inner publishers are both constant, we need to pass on this
25+
// characteristic to the outside by using the appropriate initializer.
26+
return CurrentValuePublisher<T, Failure>(value: currentInnerPublisher.value)
27+
default:
28+
let initial = currentInnerPublisher.value
29+
30+
let upstream = Publishers.Merge(
31+
// The publisher of a single publisher of remaining values produced by the current
32+
// inner publisher (excluding its current value).
33+
Just(currentInnerPublisher.dropFirst().eraseToAnyPublisher())
34+
.setFailureType(to: Failure.self),
35+
36+
// The publisher of subsequent inner publishers.
37+
self.dropFirst()
38+
.map(transform)
39+
.map { $0.eraseToAnyPublisher() }
40+
).switchToLatest()
41+
42+
return CurrentValuePublisher<T, Failure>(
43+
initial: initial,
44+
upstream: upstream
45+
)
46+
}
47+
}
48+
49+
}

Tests/CombineExtensionsTests/CurrentValuePublisher/CurrentValuePublisherTests.swift

Lines changed: 131 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -476,137 +476,137 @@ class CurrentValuePublisherTests: XCTestCase {
476476
XCTAssertEqual(values, ["1", "2"])
477477
}
478478

479-
// // ============================================================================ //
480-
// // MARK: - FlatMap Operator
481-
// // ============================================================================ //
482-
//
483-
// func testFlatMap_constant_constant() {
484-
// var cancellables = Set<AnyCancellable>()
485-
// var values = [String]()
486-
//
487-
// CurrentValuePublisher(value: 1)
488-
// .flatMapLatest { CurrentValuePublisher(value: "\($0)") }
489-
// .sink { values.append($0) }
490-
// .store(in: &cancellables)
491-
//
492-
// XCTAssertEqual(values, ["1"])
493-
// }
494-
//
495-
// func testFlatMap_constant_subject() {
496-
// let subject = CurrentValueSubject<Int, Never>(1)
497-
// let publisher = subject.toCurrentValuePublisher()
498-
//
499-
// var cancellables = Set<AnyCancellable>()
500-
// var values = [String]()
501-
//
502-
// CurrentValuePublisher(value: ())
503-
// .flatMapLatest { _ in publisher.map { "\($0)" } }
504-
// .sink { values.append($0) }
505-
// .store(in: &cancellables)
506-
//
507-
// XCTAssertEqual(values, ["1"])
508-
//
509-
// subject.value = 2
510-
// XCTAssertEqual(values, ["1", "2"])
511-
// }
512-
//
513-
// func testFlatMap_subject_constants() {
514-
// let subject = CurrentValueSubject<Int, Never>(1)
515-
// let publisher = subject.toCurrentValuePublisher()
516-
//
517-
// var cancellables = Set<AnyCancellable>()
518-
// var values = [String]()
519-
//
520-
// publisher
521-
// .flatMapLatest { CurrentValuePublisher(value: "\($0)") }
522-
// .sink { values.append($0) }
523-
// .store(in: &cancellables)
524-
//
525-
// XCTAssertEqual(values, ["1"])
526-
//
527-
// subject.value = 2
528-
// XCTAssertEqual(values, ["1", "2"])
529-
//
530-
// subject.value = 2
531-
// XCTAssertEqual(values, ["1", "2", "2"])
532-
// }
533-
//
534-
// func testFlatMap_subject_subjects() {
535-
// let innerSubject1 = CurrentValueSubject<Int, Never>(10)
536-
// let innerPublisher1 = innerSubject1.toCurrentValuePublisher()
537-
//
538-
// let innerSubject2 = CurrentValueSubject<Int, Never>(20)
539-
// let innerPublisher2 = innerSubject2.toCurrentValuePublisher()
540-
//
541-
// let outerSubject = CurrentValueSubject<CurrentValuePublisher<Int, Never>, Never>(innerPublisher1)
542-
// let outerPublisher = outerSubject.toCurrentValuePublisher()
543-
//
544-
// var cancellables = Set<AnyCancellable>()
545-
// var values = [String]()
546-
//
547-
// outerPublisher
548-
// .flatMapLatest { innerPublisher in
549-
// innerPublisher.map { "\($0)" }
550-
// }
551-
// .sink { values.append($0) }
552-
// .store(in: &cancellables)
553-
//
554-
// XCTAssertEqual(values, ["10"])
555-
//
556-
// innerSubject1.value = 11
557-
// XCTAssertEqual(values, ["10", "11"])
558-
//
559-
// outerSubject.value = innerPublisher2
560-
// innerSubject2.value = 21
561-
// innerSubject2.value = 22
562-
// XCTAssertEqual(values, ["10", "11", "20", "21", "22"])
563-
//
564-
// innerSubject1.value = 12
565-
// outerSubject.value = innerPublisher1
566-
// innerSubject1.value = 13
567-
// XCTAssertEqual(values, ["10", "11", "20", "21", "22", "12", "13"])
568-
// }
569-
//
570-
// func testFlatMap_subject_subjectsAndConstants() {
571-
// let innerSubject1 = CurrentValueSubject<Int, Never>(10)
572-
// let innerPublisher1 = innerSubject1.toCurrentValuePublisher()
573-
//
574-
// let innerPublisher2 = CurrentValuePublisher<Int, Never>(value: 20)
575-
// let innerPublisher3 = CurrentValuePublisher<Int, Never>(value: 30)
576-
//
577-
// let outerSubject = CurrentValueSubject<CurrentValuePublisher<Int, Never>, Never>(innerPublisher1)
578-
// let outerPublisher = outerSubject.toCurrentValuePublisher()
579-
//
580-
// var cancellables = Set<AnyCancellable>()
581-
// var values = [String]()
582-
//
583-
// outerPublisher
584-
// .flatMapLatest { innerPublisher in
585-
// innerPublisher.map { "\($0)" }
586-
// }
587-
// .sink { values.append($0) }
588-
// .store(in: &cancellables)
589-
//
590-
// XCTAssertEqual(values, ["10"])
591-
//
592-
// innerSubject1.value = 11
593-
// XCTAssertEqual(values, ["10", "11"])
594-
//
595-
// outerSubject.value = innerPublisher2
596-
// XCTAssertEqual(values, ["10", "11", "20"])
597-
//
598-
// innerSubject1.value = 12
599-
// outerSubject.value = innerPublisher1
600-
// XCTAssertEqual(values, ["10", "11", "20", "12"])
601-
//
602-
// outerSubject.value = innerPublisher3
603-
// outerSubject.value = innerPublisher3
604-
// XCTAssertEqual(values, ["10", "11", "20", "12", "30", "30"])
605-
//
606-
// outerSubject.value = innerPublisher1
607-
// XCTAssertEqual(values, ["10", "11", "20", "12", "30", "30", "12"])
608-
// }
609-
//
479+
// ============================================================================ //
480+
// MARK: - FlatMapLatest Operator
481+
// ============================================================================ //
482+
483+
func testFlatMapLatest_constant_constant() {
484+
var cancellables = Set<AnyCancellable>()
485+
var values = [String]()
486+
487+
CurrentValuePublisher(value: 1)
488+
.flatMapLatest { CurrentValuePublisher(value: "\($0)") }
489+
.sink { values.append($0) }
490+
.store(in: &cancellables)
491+
492+
XCTAssertEqual(values, ["1"])
493+
}
494+
495+
func testFlatMapLatest_constant_subject() {
496+
let subject = CurrentValueSubject<Int, Never>(1)
497+
let publisher = CurrentValuePublisher(subject)
498+
499+
var cancellables = Set<AnyCancellable>()
500+
var values = [String]()
501+
502+
CurrentValuePublisher(value: ())
503+
.flatMapLatest { _ in publisher.map { "\($0)" } }
504+
.sink { values.append($0) }
505+
.store(in: &cancellables)
506+
507+
XCTAssertEqual(values, ["1"])
508+
509+
subject.value = 2
510+
XCTAssertEqual(values, ["1", "2"])
511+
}
512+
513+
func testFlatMapLatest_subject_constants() {
514+
let subject = CurrentValueSubject<Int, Never>(1)
515+
let publisher = CurrentValuePublisher(subject)
516+
517+
var cancellables = Set<AnyCancellable>()
518+
var values = [String]()
519+
520+
publisher
521+
.flatMapLatest { CurrentValuePublisher(value: "\($0)") }
522+
.sink { values.append($0) }
523+
.store(in: &cancellables)
524+
525+
XCTAssertEqual(values, ["1"])
526+
527+
subject.value = 2
528+
XCTAssertEqual(values, ["1", "2"])
529+
530+
subject.value = 2
531+
XCTAssertEqual(values, ["1", "2", "2"])
532+
}
533+
534+
func testFlatMapLatest_subject_subjects() {
535+
let innerSubject1 = CurrentValueSubject<Int, Never>(10)
536+
let innerPublisher1 = CurrentValuePublisher(innerSubject1)
537+
538+
let innerSubject2 = CurrentValueSubject<Int, Never>(20)
539+
let innerPublisher2 = CurrentValuePublisher(innerSubject2)
540+
541+
let outerSubject = CurrentValueSubject<CurrentValuePublisher<Int, Never>, Never>(innerPublisher1)
542+
let outerPublisher = CurrentValuePublisher(outerSubject)
543+
544+
var cancellables = Set<AnyCancellable>()
545+
var values = [String]()
546+
547+
outerPublisher
548+
.flatMapLatest { innerPublisher in
549+
innerPublisher.map { "\($0)" }
550+
}
551+
.sink { values.append($0) }
552+
.store(in: &cancellables)
553+
554+
XCTAssertEqual(values, ["10"])
555+
556+
innerSubject1.value = 11
557+
XCTAssertEqual(values, ["10", "11"])
558+
559+
outerSubject.value = innerPublisher2
560+
innerSubject2.value = 21
561+
innerSubject2.value = 22
562+
XCTAssertEqual(values, ["10", "11", "20", "21", "22"])
563+
564+
innerSubject1.value = 12
565+
outerSubject.value = innerPublisher1
566+
innerSubject1.value = 13
567+
XCTAssertEqual(values, ["10", "11", "20", "21", "22", "12", "13"])
568+
}
569+
570+
func testFlatMapLatest_subject_subjectsAndConstants() {
571+
let innerSubject1 = CurrentValueSubject<Int, Never>(10)
572+
let innerPublisher1 = CurrentValuePublisher(innerSubject1)
573+
574+
let innerPublisher2 = CurrentValuePublisher<Int, Never>(value: 20)
575+
let innerPublisher3 = CurrentValuePublisher<Int, Never>(value: 30)
576+
577+
let outerSubject = CurrentValueSubject<CurrentValuePublisher<Int, Never>, Never>(innerPublisher1)
578+
let outerPublisher = CurrentValuePublisher(outerSubject)
579+
580+
var cancellables = Set<AnyCancellable>()
581+
var values = [String]()
582+
583+
outerPublisher
584+
.flatMapLatest { innerPublisher in
585+
innerPublisher.map { "\($0)" }
586+
}
587+
.sink { values.append($0) }
588+
.store(in: &cancellables)
589+
590+
XCTAssertEqual(values, ["10"])
591+
592+
innerSubject1.value = 11
593+
XCTAssertEqual(values, ["10", "11"])
594+
595+
outerSubject.value = innerPublisher2
596+
XCTAssertEqual(values, ["10", "11", "20"])
597+
598+
innerSubject1.value = 12
599+
outerSubject.value = innerPublisher1
600+
XCTAssertEqual(values, ["10", "11", "20", "12"])
601+
602+
outerSubject.value = innerPublisher3
603+
outerSubject.value = innerPublisher3
604+
XCTAssertEqual(values, ["10", "11", "20", "12", "30", "30"])
605+
606+
outerSubject.value = innerPublisher1
607+
XCTAssertEqual(values, ["10", "11", "20", "12", "30", "30", "12"])
608+
}
609+
610610
// // ============================================================================ //
611611
// // MARK: - Remove Duplicates Operator
612612
// // ============================================================================ //

0 commit comments

Comments
 (0)