Recursive calls using RxSwift Observables - ios

I am trying to do a recursive call using RxSwift Observables.
import RxSwift
func observeUntil(initialValue: Int) -> Observable<Int> {
return Observable.deferred {
.just(initialValue)
}
.do(onNext: {
print("current item is", $0)
})
.flatMapLatest{ (item) -> Observable<Int> in
if item < 5 {
return Observable.just(item)
// .delay(.seconds(1), scheduler: MainScheduler.instance)
.flatMapLatest{observeUntil(initialValue: $0 + 1)}
} else {
return .just(item)
}
}
}
_ = observeUntil(initialValue: 0)
.subscribe()
When I comment the delay in above code, the output comes correctly like below
current item is 0
current item is 1
current item is 2
current item is 3
current item is 4
current item is 5
Program ended with exit code: 0
with delay the code only outputs
current item is 0
Program ended with exit code: 0
Please help me to understand what happens when the delay is added.

The answer has to do with the environment you are executing this code in. The program calls the observeUntil(initialValue:) function and then exists as soon as that function returns.
Without the delay the function returns after the recursive code is all executed. With the delay the function returns when the delay starts.
Basically, your program ends in less than a second so only "0" is output.

To clarify Daniel T.'s answer, as soon as your code
_ = observeUntil(initialValue: 0)
.subscribe()
goes out of scope, the subscription is disposed of, which is why you see it without the delay, but adding the delay will end up not executing the rest of the sequence until after it has been disposed of. The class which subscribes to the observable should have a DisposeBag which will retain the subscription when it goes out of scope.
// As an instance variable in your class, or some other place you want to retain the subscription
let disposeBag = DisposeBag()
// Where you subscribe
observeUntil(initialValue: 0)
.subscribe()
.disposed(by: myClass.disposeBag)
When the dispose bag goes out of scope, so will your subscription and it will be disposed of, terminating the sequence emitted.

Related

RXSwift Not subscribing on Main Thread

I am trying to make several API calls and populate a Realm Database.
Everything works fine. However when I try to run performSegue() on subscribe() method an exception is raised, informing that I can't do this on a background thread, which is perfectly reasonable.
But since I am subscribing to MainScheduler.instance shouldn't the subscribe() method run on UI Thread?
Single.zip(APIClient.shared.getSchools(), APIClient.shared.getPointsOfInterest())
.observeOn(SerialDispatchQueueScheduler(qos: .background))
.flatMap { zip in return Single.zip(SchoolDao.shared.insertSchools(schoolsJson: zip.0), PointOfInterestDao.shared.insertPointsOfInterest(poisJson: zip.1))}
.flatMap{ _ in Single.zip(SchoolDao.shared.countSchools(), PointOfInterestDao.shared.countPointsOfInterest())}
.subscribeOn(MainScheduler.instance)
.subscribe(onSuccess: { tableCounts in
let (schoolsCount, poisCount) = tableCounts
if(schoolsCount != 0 && poisCount != 0){
print(Thread.isMainThread) //Prints False
self.performSegue(withIdentifier: "splashToLogin", sender: nil)
}
}, onError: {
error in return
}).disposed(by: disposeBag)
Am I making a wrong assumption on how does RXSwift works?
Edit: If I add this line .observeOn(MainScheduler.instance) after .subscribeOn(MainScheduler.instance) the subscribe method runs on Main thread. Is this correct behavior? What is .subscribeOn(MainScheduler.instance) even doing?
Your edit explains all. Your initial assumption on what subscribeOn and observeOn were backwards.
The subscribeOn operator refers to how the observable above the operator in the chain subscribes to the source of events (and likely doesn't do what you think it does in any case. Your two network calls likely set up their own background thread to emit values on regardless of how they are subscribed to.)
For example, look at this:
extension ObservableType {
func subscribeOnMain() -> Observable<Element> {
Observable.create { observer in
let disposable = SingleAssignmentDisposable()
DispatchQueue.main.async {
disposable.setDisposable(self.subscribe(observer))
}
return disposable
}
}
}
It makes it obvious why the operator is called subscribeOn. It's because the subscribe is happening on the scheduler/thread in question. And this helps you understand better what is happening when you stack subscribeOn operators...
The observeOn operator refers to the scheduler that will be emitting elements to the observer (which is the block(s) of code that are passed to the subscribe operator.)
Which would look like this:
extension ObservableType {
func observeOnMain() -> Observable<Element> {
Observable.create { observer in
self.subscribe { event in
DispatchQueue.main.async {
observer.on(event)
}
}
}
}
}
From this you can see that the subscribe is happening on the original scheduler, while the observer is being called on the new scheduler.
Here is a great article explaining the whole thing: http://rx-marin.com/post/observeon-vs-subscribeon/

Collecting stored variable property using withLatestFrom

I'm wondering if there is a way in RxSwift to observe value of stored variable property. Eg. in following example:
var updatedValue: Int = 0
var observedValue: Observable<Int> {
return Observable.create({ (observer) -> Disposable in
observer.onNext(updatedValue)
return Disposables.create()
})
}
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
updatedValue = updatedValue + 1;
}
let myObservable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.publish()
myObservable.connect()
myObservable
.withLatestFrom(observedValue)
.subscribe { (event) in
print(event)
}
We have variable property updatedValue and hot observable myObservable. We also increment value of updatedValue in Timer.scheduledTimer....
Flow here is pretty straight forward. When we subscribe, observedValue gets called, we get onNext from observedValue and then Disposables.create(). Then we print event onNext(0).
As myObservable is based on Observable.interval, same withLatestFrom value gets printed in onNext every second.
Question: Is there a way to print last value of updatedValue every time myObservable emits new event? So instead of 0,0,0... we get 0,1,2...
I'm aware that updatedValue could be declared as BehaviorRelay.
I'm also aware that we could use .map { } to capture self.updatedValue.
But I'm wondering if there is any way to create a Observable wrapper around standard variable property so it calls onNext with most recent value every time trigger sequence sends an event? Without capturing self or changing declaration on updatedValue.
Thanks for any comments and ideas!
RxCocoa has a handy wrapper around KVO. You should be able to use it from .rx extension on NSObject subclasses.
For your issue, I guess you can do something like:
let updatedValueObservable = self.rx.observe(Int.self, "updatedValue")
But I'm wondering if there is any way to create a Observable wrapper around standard variable property so it calls onNext with most recent value every time trigger sequence sends an event? Without capturing self or changing declaration on updatedValue.
The correct answer is, no. There is no way to do anything to updatedValue without involving self. One way of doing it would be with Observable<Int>.interval(1, scheduler: MainScheduler.instance).compactMap { [weak self] _ in self?.updatedValue }.distinctUntilChanged() (Your use of publish and connect is odd and unnecessary,) but that involves self.
Since your property is a value type, the only way to access it is through self, even if Rx wasn't involved at all.

Blocking Thread with RxSwift

Below is the simplified version of my project I'm struggling now.
I'd like to block the thread until first Single succeeds.
These observables should be two separated streams but sharing one thread.
Do you have any idea of fixing this code? Thanks!
let disposeBag = DisposeBag()
var hasReturnedSingle = false
func returnSingle() -> Single<String> {
return Single<String>.create { single in
print("hasReturnedSingle: \(hasReturnedSingle)")
DispatchQueue.main.asyncAfter(deadline: .now()+3.0) {
single(.success(()))
hasReturnedSingle = true
}
return Disposables.create()
}
}
returnSingle().asObservable()
.subscribeOn(MainScheduler.instance)
.asSingle()
.subscribe()
.disposed(by: disposeBag)
returnSingle().asObservable()
.subscribeOn(MainScheduler.instance)
.asSingle()
.subscribe()
.disposed(by: disposeBag)
Result:
hasReturnedSingle: false
hasReturnedSingle: false
Expectation:
hasReturnedSingle: false
hasReturnedSingle: true
The subscribeOn operator affects the thread that the producer starts on, i.e the thread that the print statement runs on, but not the thread that DispatchQueue runs on. So the first call prints, pushes the block onto the main dispatch queue then completes. Then the second call does the same. Then the dispatches fire.
You can fix the problem any of a number of ways depending on how much control you have over the code in the producer function:
write the producer function in a sequential format instead of dispatching.
pass a scheduler into your returnSingle function and then use it instead of the DispatchQueue.
run all the operations into a subject and then concatMap the subject's emissions.

RxSwift: Why does flatMapLatest never execute onCompleted()?

In my Swift UIViewController, I'm attempting to subscribe to a class member of type Variable, run it through a flatMapLatest call, and then have the onCompleted() call in the flatMapLatest observable execute on all subscribers. However, while onNext() is called, onCompleted() never is and I'm not sure why.
My class member is defined as:
private let privateVar = Variable<String>("")
while in my viewDidLoad() method, I set up the observables:
let localVar = self.privateVar.asObservable().distinctUntilChanged()
localVar.subscribe(onNext: { [weak self] sent in print("first onNext called") })
.disposed(by: self.disposeBag)
let mappedVar = localVar.flatMapLatest { self.ajaxLoad(var1: $0) }.share()
mappedVar.subscribe(
onNext: { [weak self] queryRes in
print("onNext called!")
},
onCompleted: { [weak self] in
print("onCompleted called!")
}
)
.disposed(by: self.disposeBag)
and my ajaxLoad method:
func ajaxLoad(var1 myVar: String) -> Observable<QueryResponse> {
return Observable.create { observable in
apollo.fetch(query: MyQuery()) { (result, _) in
observable.onNext(result?.data?.myQuery)
observable.onCompleted()
}
return Disposables.create()
}
}
I'm fairly new to ReactiveX so I may be a little hazy on what the Rx lifecycle actually looks like. Why might onNext be called in the flatMapLatest call, but not onCompleted? Any help would be appreciated. Thanks in advance!
The flatMap operator does not emit completed events of any observable that you return inside the block.
The following code illustrates this clearly. .just(_) emits the element and then a completed event, which does not terminate to subscription.
_ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.debug("before flatmap")
.flatMap { .just($0 * 2) }
.debug("after flatmap")
.subscribe()
In fact, Variable only emits completed when deallocated. See source v4.0.
Note that Variable is deprecated in RxSwift 4, you are encouraged to use RxCocoa's similar BehaviorRelay instead.
deinit {
_subject.on(.completed)
}
Since you said you are new and a "little hazy"...
Keep in mind that whenever localVar changes, it emits a new value and ajaxLoad(var1:) gets called. The result of ajaxLoad(var1:) then gets pushed to your subscribe's onNext closure.
Also keep in mind that if an Observable emits a .completed it's dead. It can no longer emit anything else.
So flatMapLatest can't complete (unless its source completes.) If it did, it would kill the whole pipe and no more changes to localVar would get routed through the pipe, ajaxLoad(var:1) wouldn't get called again with the new value and nothing more would get pushed to the subscribe's onNext method.
A sequence of observables can be thought of like a Rube Goldberg machine where a completed shuts down the machine and an error breaks it. The only time you should shut down the machine is if the source (in this case localVar) is finished emitting values or the sink (destination, in this case the onNext: closure) doesn't want any more values.

How to "loosely" trigger a function?

I have the following async recursive code:
func syncData() {
dal.getList(...) { [unowned self] list, error in
if let objects = list {
if oneTime {
oneTime = false
syncOtherStuffNow()
}
syncData() // recurse until all data synced
} else if let error = error {... }
func syncOtherStuffNow() { } // with its own recursion
My understanding is that the recursion will build the call stack until all the function calls complete, at which point they will all unwind and free up the heap.
I also want to trigger another function (syncOtherStuffNow) from within the closure. But don't want to bind it to the closure with a strong reference waiting for it's return (even though it's async too).
How can I essentially trigger the syncOtherStuffNow() selector to run, and not affect the current closure with hanging on to its return call?
I thought of using Notifications, but that seems overkill given the two functions are in the same class.
Since dal.getList() takes a callback I guess it is asynchronous and so the the first syncData starts the async call and then returns immediately which lets syncData() return.
If syncOtherStuffNow() is async it will return immediately and so dataSync() will not wait on it finishing its job and so continue with its execution to the end.
You can test whether sth builds a callstack by putting a breakpoint on every recursion and look on the callstack how many calls of the same function are ontop.
What I do is recurse with asyncAfter, which unwinds the call stack.

Resources