SwiftUI and CombineLatest with more than 4 values

Viewed 3506

I'm starting to experiment with SwiftUI and I have a situation where I want the latest combination of 5 sliders. I had everything working with 4 sliders, using CombineLatest4, then realized I need another slider, but there's no CombineLatest5.

Any help appreciated.

To clarify the working 4-slider version:

Publishers
    .CombineLatest4($slider1, $slider2, $slider3, $slider4)
    .debounce(for: 0.3, scheduler: DispatchQueue.main)
    .subscribe(subscriber)
4 Answers

CombineLatest2(CombineLatest3, CombineLatest2) should do the trick, shouldn't it?

Note - solution extracted from the question


Okay, getting the syntax figured out, @Daniel-t was right. I just needed to create the sub-publishers:

let paramsOne = Publishers
    .CombineLatest3($slider1, $slider2, $slider3)
        
let paramsTwo = Publishers
    .CombineLatest($slider4, $slider5)
        
paramsOne.combineLatest(paramsTwo)
    .debounce(for: 0.3, scheduler: DispatchQueue.main)
    .subscribe(subscriber)

Note that I also had to change what my subscriber expected as input from (Double, Double, Double, Double, Double) to ((Double, Double, Double), (Double, Double)), and that the compiler gave me a misleading and confusing error (something about Schedulers) until I figured out that the input type was wrong.

You can use something like:

extension Publisher {
public func combineLatest<P, Q, R, Y>(
    _ publisher1: P,
    _ publisher2: Q,
    _ publisher3: R,
    _ publisher4: Y) ->
    AnyPublisher<(Self.Output, P.Output, Q.Output, R.Output, Y.Output), Self.Failure> where
    P: Publisher,
    Q: Publisher,
    R: Publisher,
    Y: Publisher,
    Self.Failure == P.Failure,
    P.Failure == Q.Failure,
    Q.Failure == R.Failure,
    R.Failure == Y.Failure {
    Publishers.CombineLatest(combineLatest(publisher1, publisher2, publisher3), publisher4).map { tuple, publisher4Value in
        (tuple.0, tuple.1, tuple.2, tuple.3, publisher4Value)
    }.eraseToAnyPublisher()
  }
}

and call it like:

publisher1.combineLatest(publisher2, publisher3, publisher4, publisher5)

Expanding on Leonid's answer: if you need support for a transform function, you need something like this:

    public func combineLatest<P, Q, R, Y, T>(
            _ publisher1: P,
            _ publisher2: Q,
            _ publisher3: R,
            _ publisher4: Y,
            _ transform: @escaping (Self.Output, P.Output, Q.Output, R.Output, Y.Output) -> T) ->
            AnyPublisher<T, Self.Failure> where
    P: Publisher,
    Q: Publisher,
    R: Publisher,
    Y: Publisher,
    Self.Failure == P.Failure,
    P.Failure == Q.Failure,
    Q.Failure == R.Failure,
    R.Failure == Y.Failure {
        Publishers.CombineLatest(combineLatest(publisher1, publisher2, publisher3), publisher4)
                  .map { tuple, publisher4Value in
                      transform(tuple.0, tuple.1, tuple.2, tuple.3, publisher4Value)
                  }
                  .eraseToAnyPublisher()
    }
Related