Reputation: 3943
My actual requirement:
I have a list of custom objects, and I want to iterate it with a delay. I can't use DispatchQueue.main.asyncAfter
in my for
loop since my iterations create a CoreData
object that triggers FetchedResultController
and hence updates my TableView
. Anyway, so I tried using Rx to iterate my list with a delay of 1 second each. But I am unable to do so.
Question
I want to delay the iteration of each element of the array using RxSwift. I was able to do it in Java, but couldn't do so in RxSwift.
the .delay()
operator didn't help either, it just delayed the whole process.
Any example would help, thus I am not posting any specific code... but this is what I've been trying so far
var array = [1, 2, 3, 4, 5]
Observable.from(array)
.delay(RxTimeInterval(5), scheduler: MainScheduler.instance)
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
Output
onNext() next(1)
onNext() next(2)
onNext() next(3)
onNext() next(4)
onNext() next(5)
onNext() completed
The output gets printed after 5 seconds, not with 5 seconds interval.
I am not getting any integer values, but a next(1)
.
Upvotes: 1
Views: 1567
Reputation: 33979
This is a common confusion. As you have learned delay
will be applied to every element equally so all of them are delayed by five seconds, rather than putting a five second delay between each event. (So the first event happens immediately, the second at five seconds, the third at ten, and so on.
Once you realize that what you are really trying to do is put a delay between each event, the solution becomes more clear. Or at least, the reason why just putting a delay on the from
operator isn't working should be more clear.
The solution is to use one of the flatMap variants:
let array = [1, 2, 3, 4, 5]
Observable.from(array)
.concatMap { Observable.empty().delay(.seconds(5), scheduler: MainScheduler.instance).startWith($0) }
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
The line Observable.empty().delay(.seconds(5), scheduler: MainScheduler.instance).startWith($0)
will immediately emit the value, but then wait five seconds before emitting a completed event.
The concatMap
operator calls the closure on each incoming event and concats the resulting Observables together so that following Observables aren't subscribed to, until the previous one completes.
Learn more about this in my article, The Many Faces of FlatMap
An alternative solution based on the comments that Sandeep Bhandari wrote on the question would be to use an interval to constrain when the from
operator can emit values. Something like this:
let array = [1, 2, 3, 4, 5]
Observable.zip(Observable.from(array), Observable<Int>.interval(.seconds(5), scheduler: MainScheduler.instance).take(while: { $0 != array.count }))
.map { $0 }
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
Upvotes: 2