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.
Related
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.
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/
I'm learning RxSwift and I've come across the following pattern when creating Observables:
return Observable.create { observer in
let disposable = Disposables.create()
// Do some stuff with observer here
return disposable
}
As far as I can tell the returned Disposable doesn't actually do anything, does it serve a purpose other than to meet the requirements of the API to return a Disposable?
Is there any scenario where you might need to return a configured Disposable?
I suppose the thing that's confusing me the most is that the returned Disposable seems separate from the implementation of the Observable being created, i.e. it's not assigned to any properties or passed anywhere it's just created and returned.
There are two variations of the create method in relation to Disposables.
The first one, as Daniel mentioned, is used when you create a new Observable; you'll use the Disposables.create { ... } closure to "do cleanup", basically.
This is highly useful when using flatMapLatest, as your previous request will be disposed when a new ones comes in. Whenever it would be disposed, that "clean up" block will be called.
Observable<Int>.create { observer in
let someRequest = doSomeLongRunningThing { result in
observer.onNext(result)
observer.onCompleted()
}
return Disposables.create {
// How can I "cleanup" the process?
// Cancel the request, for example.
someRequest.cancel()
}
}
The second variation of Disposables.create is used for an entirely different purpose - grouping several Disposable objects as a single disposable object (a CompositeDisposable).
For example:
let disposable1 = someAction()
let disposable2 = someOtherAction()
let compositeDisposable = Disposables.create(disposable1, disposable2)
The Disposables.create function takes an optional closure. You should put any cancelation code in that closure. If you don't have any way to cancel, then the code is empty.
A good example is the wrapper around URLSession's dataTask method. In non-Rx code when you call URLRequest.shared.dataTask it returns a URLSessionDataTask object which can be used to cancel the network call. That object's cancel function gets called in the disposable.
Another common use is when you subscribe to some other observable from within your create closure. You then have to pass the disposable from that/those subscriptions by returning a Disposables.create(myDisposable) So that those subscriptions will get canceled properly when your Observable is disposed of.
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.
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.