How to chain signal producers or sequence of signal producers using reactive cocoa 5 or 6? - swift

I am pretty much noob in ReactiveCocoa/ReactiveSwift. I have two SignalProducers. If first SignalProducer returns nil, then I want to execute second Signal Producer otherwise not. I read the documentation, but I am not sure which syntax helps me to work something like this. Any help is highly appreciated.

Ok, so you want to take values from the first SignalProducer as long as these values are not nil. Then, you want to take values from the second SignalProducer. If phrased this way, it already tells you which operators you need: take(while:) and then:
let producerA: SignalProducer<Int?, NoError>
let producerB: SignalProducer<Int?, NoError>
...
producerA
.take(while: { $0 != nil })
.then(producerB)
The take(while:) operator will just forward all events as long as the given block returns true. So in this case, as soon as an event is nil, the block returns false and the resulting SignalProducer completes.
The then operator also forwards events from producerA until producerA completes, at which point producerB is started and now events from producerB are forwarded.

Related

RxSwift - How does merge handle simultaneous events?

So I have a code that looks something like this:
let a = Observable.just("A").delay(.seconds(3), scheduler: MainScheduler.instance)
let b = Observable.just("B").delay(.seconds(3), scheduler: MainScheduler.instance)
Observable.merge([a, b]).subscribe(onNext: { print($0) })
I thought that printed order should be random, as the delay that finishes marginally earlier will be emitted first by merge. But the order seems to be determined solely by the order of variables in array passed to merge.
That means .merge([a, b]) prints
A
B
but .merge([b, a]) prints
B
A
Why is this the case? Does it mean that merge somehow handles the concurrent events and I can rely on the fact that the events with same delay will always be emitted in their order in array?
I know I could easily solve it by using .from(["A", "B"]), but I am now curious to know how exactly does the merge work.
You put both delays on the same thread (and I suspect the subscriptions are also happening on that thread,) so they will execute on the same thread in the order they are subscribed to. Since the current implementation of merge subscribes to the observables in the order it receives them, you get the behavior you are seeing. However, it's important to note that you cannot rely on this, there is no guarantee that it will be true for every version of the library.

Confused about Observable vs. Single in functions like readCharacteristic()

In the RxJava2 version of RxAndroidBle, the functions readCharacteristic() and writeCharacteristic() return Single<byte[]>.
The example code to read a characteristic is:
device.establishConnection(false).flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
But the documentation for flatMap() says the mapping function is supposed to return an ObservableSource. Here, it returns a Single. How can this work?
Update: I looked at possibilities using operators like .single() and .singleOrError() but they all seem to require that the upstream emits one item and then completes. But establishConnection() doesn't ever complete. (This is one reason I suggested that perhaps establishConnection() should be reimagined as a Maybe, and some other way be provided to disconnect rather than just unsubscribing.)
You're totally correct, this example cannot be compiled. it's probably leftover from RxJava1 version, where Single wasn't exists.
Simple fix with the same result is to use RxJava2 flatMapSingle for instance:
device.establishConnection(false)
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
flatMapSingle accepts a Single as the return value, and will map the success value of the input Single to an emission from the upstream Observable.
The point is, that RxJava has more specific Observable types, that exposes the possible series of emission expected from this Observable. Some methods now return Single as this is the logical operation of their stream (readCharacteristic()), some Observable as they will emit more than single emission (establishConnection() - connection status that can be changed over time).
But RxJava2 also provided many operators to convert between the different types and it really depends on your needs and scenario.
Thanks Rob!
In fact, the README was deprecated and required some pimping here and there. Please have a look if it's ok now.
I think I found the answer I was looking for. The crucial point:
Single.fromObservable(observableSource) doesn't do anything until it receives the second item from observableSource! Assuming that the first item it receives is a valid emission, then if the second item is:
onComplete(), it passes the first item to onSuccess();
onNext(), it signals IndexOutOfBoundsException since a Single can't emit more than one item;
onError(), it presumably forwards the error downstream.
Now, device.establishConnection() is a 1-item, non-completing Observable. The RxBleConnecton it emits is flatMapped to a Single with readCharacteristic(). But (another gotcha), flatMapSingle subscribes to these Singles and combines them into an Observable, which doesn't complete until the source establishConnection() does. But the source doesn't ever complete! Therefore the Single we're trying to create won't emit anything, since it doesn't receive that necessary second item.
The solution is to force the generation of onComplete() after the first (and only) item, which can be done with take(1). This will satisfy the Single we're creating, and cause it to emit the Characteristic value we're interested in. Hope that's clear.
The code:
Single<byte[]> readCharacteristicSingle( RxBleDevice device, UUID characteristicUUID ) {
return Single.fromObservable(
device.establishConnection( false )
.flatMapSingle( connection -> connection.readCharacteristic( characteristicUUID ) )
.take( 1L ) // make flatMapSingle's output Observable complete after the first emission
// (this makes the Single call onSuccess())
);
}

Flatmap my observable to subject

The question is a little tricky.
I am trying to implement the observable interface, within it i need to start listen to another publicsubject once the observable meet some circustance, so i write some code like this:
public myAPI(){
return restAPI.call()
.flatmap{ ret ->
if(ret == success) return myPublishSubject
}
can it guarantee the subscribe start subscribe to the publishsubject only after restAPI call is done successfully ?
The flatMap's Function callback is invoked when there is a value from upstream, in this case, the restAPI.call().
However, note that mapping to a PublishSubject late can result in items being missed. To avoid such problems, you can consider using BehaviorSubject that retains the last item it received so the flatMap can emit immediately upon subscribing to it.
In addition, repeatedly mapping to the same Subject can result in memory leaks and item duplication. Unfortunately, you'd have to complete the Subject in order to release it, but then it becomes unusable for dispatching further events. takeUntil may help in this case though.

Does the order of subscribeOn and observeOn matter?

I'm a little bit confused about the order you can call the subscribeOn and observeOn methods on observables. I read a couple of posts and one guys says that it doesn't matter and just uses thing in his example and other people say it does matter. So here is my question:
For example:
self.remoteService.rxGetAllLanguages()
.observeOn(MainScheduler.instance)
.subscribeOn(ConcurrentDispatchQueueScheduler(globalConcurrentQueueQOS: .Background))
.subscribe({ e in
switch e {
case .Next(let element):
case .Error(let e):
DDLogError("Error in \(e)")
case .Completed:
DDLogDebug("Completed")
}
}
).addDisposableTo(self.disposeBag)
Is that the same as:
self.remoteService.rxGetAllLanguages()
.subscribeOn(ConcurrentDispatchQueueScheduler(globalConcurrentQueueQOS: .Background))
.observeOn(MainScheduler.instance)
.subscribe({ e in
switch e {
case .Next(let element):
case .Error(let e):
DDLogError("Error in \(e)")
case .Completed:
DDLogDebug("Completed")
}
}
).addDisposableTo(self.disposeBag)
If I correctly understand the mechanisms they are different. The first one does all the work on the main thread and the second does all the work on another thread and then dispatches back to the main thread. But I'm nut sure so can someone clear this for me please?
Where you call subscribeOn() in a chain doesn't really matter. Where you call observeOn() does matter.
subscribeOn() tells the whole chain which thread to start processing on. You should only call it once per chain. If you call it again lower down the stream it will have no effect.
observeOn() causes all operations which happen below it to be executed on the specified scheduler. You can call it multiple times per stream to move between different threads.
Take the following example:
doSomethingRx()
.subscribeOn(BackgroundScheduler)
.doAnotherThing()
.observeOn(ComputationScheduler)
.doSomethingElse()
.observeOn(MainScheduler)
.subscribe(//...)
The subscribeOn causes doSomethingRx to be called on the BackgroundScheduler.
doAnotherThing will continue on BackgroundScheduler
then observeOn switches the stream to the ComputationScheduler
doSomethingElse will happen on the ComputationScheduler
another observeOn switches the stream to the MainScheduler
subscribe happens on the MainScheduler
Yea you're correct. observeOn will only receive the events on the thread you've specified, whereas subscribeOn will actually execute the work within the specified thread.
.subscribeOn() operator affect on which sheduler chain will be created (to the left of it) and it work once.
.observeOn() influence operator to the right of it - on which schedulers data will be processed after operator.

Can an Rx Observable gracefully handle exceptions in an operator and continue?

i.e., by passing the error condition and not halting the entire Observable?
My Observable starts with a user-supplied list of package tracking numbers from common delivery services (FedEx, UPS, DHL, etc), looks up the expected delivery date online, then returns those dates in terms of number of days from today (i.e. "in 3 days" rather than "Jan 22"). The problem is that if any individual lookup results in an exception, the entire stream halts, and the rest of the codes won't be looked up. There's no ability to gracefully handle, say, UnknownTrackingCode Exception, and so the Observable can't guarantee that it will look up all the codes the user submitted.
public void getDaysTillDelivery(List<String> tracking_code_list) {
Observable o = Observable.from(tracking_code_list)
// LookupDeliveryDate performs network calls to UPS, FedEx, USPS web sites or APIs
// it might throw: UnknownTrackingCode Exception, NoResponse Exception, LostPackage Exception
.map(tracking_code -> LookupDeliveryDate(tracking_code))
.map(delivery_date -> CalculateDaysFromToday(delivery_date));
o.subscribe(mySubscriber); // will handle onNext, onError, onComplete
}
Halting the Observable stream as a result of one error is by design:
http://reactivex.io/documentation/operators/catch.html
Handling Exceptions in Reactive Extensions without stopping sequence
https://groups.google.com/forum/#!topic/rxjava/trm2n6S4FSc
The default behavior can be overcome, but only by eliminating many of the benefits of Rx in the first place:
I can wrap LookupDeliveryDate so it returns Dates in place of Exceptions (such as 1899-12-31 for UnknownTrackingCode Exception) but this prevents "loosely coupled code", because CalculateDaysFromToday would need to handle these special cases
I can surround each anonymous function with try/catch and blocks, but this essentially prevents me from using lambdas
I can use if/thens to direct the code path, but this will likely require maintaining some state and eliminating deterministic evaluation
Error handling of each step, obviously, prevents consolidating all error handling in the Subscriber
Writing my own error-handling operator is possible, but thinly documented
Is there a better way to handle this?
What exactly do you want to happen if there is an error? Do you just want to throw that entry away or do you want something downstream to do something with it?
If you want something downstream to take some action, then you are really turning the error into data (sort of like your example of returning a sentinel value of 1899-12-31 to represent the error). This strategy by definition means that everything downstream needs to understand that the data stream may contain errors instead of data and they must be modified to deal with it.
But rather than yielding a magic value, you can turn your Observable stream into a stream of Either values. Either the value is a date, or it is an error. Everything downstream receives this Either object and can ask it if it has a value or an error. If it has a value, they can produce a new Either object with the result of their calculation. If it has an error and they cannot do anything with it, they can yield an error Either themselves.
I don't know Java syntax, but this is what it might look like in c#:
Observable.From(tracking_code_list)
.Select(t =>
{
try { return Either.From(LookupDeliveryDate(t)); }
catch (Exception e)
{
return Either.FromError<Date>(e);
}
})
.Select(dateEither =>
{
return dateEither.HasValue ?
Either.From(CalculateDaysFromToday(dateEither.Value)) :
Either.FromError<int>(dateEither.Error);
})
.Subscribe(value =>
{
if (value.HasValue) mySubscriber.OnValue(value.Value);
else mySubscribe.OnError(value.Error);
});
Your other option is the "handle"/suppress the error when it occurs. This may be sufficient depending on your needs. In this case, just have LookupDeliveryDate return magic dates instead of exceptions and then add a .filter to filter out the magic dates before they get to CalculateDaysFromToay.