I have a publisher where I want to handle receiving the first value one way, and receiving the rest another way. Currently I have the following:
let bar = foo
.first()
.sink { value in
print("first value is \(value)")
}
let baz = foo
.dropFirst()
.sink { value in
print("one of the rest is \(value)")
}
foo.send(1)
foo.send(2)
foo.send(3)
foo.send(4)
foo.send(5)
foo.send(6)
This works. But it forces me to have two subscriptions, one for the first value, and another for the rest. Is there a clever way of combining the two into one subscription (without external flag management). Something like:
let bar = foo
.first()
.sink { value in
print("first value is \(value)")
}
.dropFirst()
.sink {
print("the rest is \(value)")
}
I'm aware the above doesn't make sense (and is probably uglier than the original solution) since a publisher is singular flow that doesn't branch (beyond value, completion, error), but that's due to my lack of creativity, I'm hoping someone here has something that I might be overlooking.
CodePudding user response:
One typical approach is to use something like .scan
to map into a tuple that tells the rest of the pipeline whether this is the first one or not.
In this simple example, the "rest of the pipeline" is simply .sink
, but you can see that it does know which one is first.
class ViewController: UIViewController {
var cancellables = Set<AnyCancellable>()
let subject = PassthroughSubject<Int, Never>()
override func viewDidLoad() {
super.viewDidLoad()
var firstPassed = false
subject
.scan((0, false)) {prev, this in
defer { firstPassed = true }
return (this, firstPassed)
}
.sink {
if !$0.1 {
print($0.0, "is the first one")
} else {
print($0.0, "is not the first one")
}
}
.store(in: &cancellables)
subject.send(1)
subject.send(2)
subject.send(3)
subject.send(4)
subject.send(5)
}
}
Result:
1 is the first one
2 is not the first one
3 is not the first one
4 is not the first one
5 is not the first one
CodePudding user response:
var cancellables = Set<AnyCancellable>()
let foo = PassthroughSubject<Int, Never>()
let first = foo
.first()
let restOfFirst = foo
.dropFirst()
Publishers.CombineLatest(first, restOfFirst) // <= here
.sink {
print("first value: \($0), rest Of other values\($1)")
}
.store(in: &cancellables)
Resualt:
foo.send(1)
foo.send(2)
foo.send(3)
foo.send(4)
foo.send(5)
foo.send(6)
first value: 1, rest Of other values: 2
first value: 1, rest Of other values: 3
first value: 1, rest Of other values: 4
first value: 1, rest Of other values: 5
first value: 1, rest Of other values: 6