Reputation: 21
I have one publisher let subject = PassthroughSubject<Human,Error>()
that emits the following objects,
subject.send(Human(fName: "John", lName: "Abraham"))
subject.send(Human(fName: "Jane", lName: "Anne"))
I need there to be a delay between these two objects, when they are being received.
example(of: "delayy(with:)"){
struct Human {
let fName: String
let lName: String
}
let subject = PassthroughSubject<Human,Error>()
let delayInSeconds = 4
var isLoading = false
subject
.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main)
subject.sink(
receiveCompletion:{
print("Received Completion", $0)
isLoading = true
print("isLoading =", isLoading)
},
receiveValue: {
print("Value Received:" ,$0)
})
subject.send(Human(fName: "John", lName: "Abraham"))
subject.send(Human(fName: "Jane", lName: "Anne"))
subject.send(completion: .finished)
}
The First value the publisher is going to be emitting here is (fName: "John", lName: "Abraham")
. The second value emitted is going to be (fName: "Jane", lName: "Anne")
.
I want there to be a delay between these two objects when the values are received here in print("Value Received:" ,$0)
I tried adding delay but it does not seem to be working. Any help is appreciated. (I am a beginner to combine)
fyi: The above code snippet is from playground
Upvotes: 2
Views: 1284
Reputation: 7744
There are several issues with your code.
You do not use the modifier properly.
subject
.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main)
subject.sink(
Here you assign the .delay
modifier to your subject. But this will return an new publisher that you simply don´t use. With the next line subject.sink
you are subscirbing to the original publisher without the delay modifier.
Proper syntax:
subject
.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue.main)
.sink(
Next, you are not storing the AnyCancellable
that is returned by your .sink
modifier. If you don´t store it, the subscription will go out of scope and you won´t receive new values.
let cancellable = subject
.delay...
.sink...
would be the proper way to store it. If you use this in a function or class instead of a Playground take care your cancellable
var does not go out of scope untill you don´t need it anymore.
With these simple issues out of the way lets look at the real problem.
The .delay
modifier won´t help you here. It just delays the entire stream of emitted values by the delay value given. This means, after sending your values the delay
function will wait for the specified time and then emit all values at once.
I tried different approaches and the only thing that worked was a combination of a .buffer
and a .flatMap
modifier.
let cancellable = subject
.buffer(size: 20000, prefetch: .byRequest, whenFull: .dropOldest)
.flatMap(maxPublishers: .max(1)) { Just($0).delay(for: .seconds(delayInSeconds), scheduler: RunLoop.main) }
.sink(
receiveCompletion:{
print("Received Completion", $0, Date())
isLoading = true
print("isLoading =", isLoading)
},
receiveValue: {
print("Value Received:" ,$0, Date())
})
The buffer is required to store the values somewhere. The .flatMap
creates new publishers from the values given but emits only one at a time. This publisher is then delayed by the time you give.
Example input:
subject.send(Human(fName: "John", lName: "Abraham"))
subject.send(Human(fName: "Jane", lName: "Anne"))
subject.send(Human(fName: "John2", lName: "Abraham"))
subject.send(Human(fName: "John3", lName: "Abraham"))
subject.send(completion: .finished)
Output:
Value Received: Human(fName: "John", lName: "Abraham") 2023-02-10 10:58:48 +0000
Value Received: Human(fName: "Jane", lName: "Anne") 2023-02-10 10:58:52 +0000
Value Received: Human(fName: "John2", lName: "Abraham") 2023-02-10 10:58:56 +0000
Value Received: Human(fName: "John3", lName: "Abraham") 2023-02-10 10:59:00 +0000
Received Completion finished 2023-02-10 10:59:00 +0000
isLoading = true
This solution has some drawbacks.
Upvotes: 4