If you want to keep using the Zip semantics, you can write a custom publisher for this. Basically, the new Zip5 will be a Zip between a Zip4 and the 5th publisher.
extension Publishers {
struct Zip5<A: Publisher, B: Publisher, C: Publisher, D: Publisher, E: Publisher>: Publisher
where A.Failure == B.Failure, A.Failure == C.Failure, A.Failure == D.Failure, A.Failure == E.Failure {
typealias Output = (A.Output, B.Output, C.Output, D.Output, E.Output)
typealias Failure = A.Failure
private let a: A
private let b: B
private let c: C
private let d: D
private let e: E
init(_ a: A, _ b: B, _ c: C, _ d: D, _ e: E) {
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
}
func receive<S>(subscriber: S) where S : Subscriber, Output == S.Input, Failure == S.Failure {
Zip(Zip4(a, b, c, d), e)
.map { ($0.0, $0.1, $0.2, $0.3, $1) }
.receive(subscriber: subscriber)
}
}
}
extension Publisher {
func zip<O1: Publisher, O2: Publisher, O3: Publisher, O4: Publisher>(_ o1: O1, _ o2: O2, _ o3: O3, _ o4: O4) -> Publishers.Zip5<Self, O1, O2, O3, O4> {
.init(self, o1, o2, o3, o4)
}
}
In a similar fashion, Zip6, Zip7 can be written:
Zip6 => Zip(Zip4(a, b, c, d, e), Zip(e, f))
Zip7 => Zip(Zip4(a, b, c, d, e), Zip3(e, f, g))
, and so on.
The downside is that this requires a lot of code to write, and if you end up needing this kind of zip operations, maybe it would be a good opportunity to re-visit the design of your app, maybe you don't need so much zipping after all.
To sustain the above, look on how would the zip6() declaration look like:
func zip<O1: Publisher, O2: Publisher, O3: Publisher, O4: Publisher, O5: Publisher>(_ o1: O1, _ o2: O2, _ o3: O3, _ o4: O4, _ o5: O5) -> Publishers.Zip6<Self, O1, O2, O3, O4, O5> {
.init(self, o1, o2, o3, o4, o5)
}
Having so many generic arguments, and constraints on all generic arguments, makes the it harder to use and understand.