Array of observables where the output of each is the input of the next - swift

I'm trying to use RxSwift to execute actions on multiple data sources. However, I have no idea how to accomplish the following.
I have an array of observabless where the output of each, should be the input of the next. So, I want to do something like, get the first observable, wait for the result and pass it to the next, all the way to the end of the array and return one final value.
Is that possible? Thanks in advance.
*** Update: Ok, I'll be more specific as requested.
The 'observables' I'm using in the array, are custom. I use a function that returns Observable.create { ... }. Inside the closure, I run an asynchronous operation that transforms the value and then send the result to the observer before completing. That resulting value, must pass to the next observable, and so on to the last observable in the array to get a final value.
The observables may send multiple values, but they must pass from one observable to the next like an assembly line.

It is difficult to know exactly what you are asking for, since Observables do not exactly have inputs but I think this is a common problem.
You may be looking for a combination of the concat or reduce operators, which allow you to accumulate data from the values emitted from an Observable. See ReactiveX's documentation for Mathematical and Aggregate Operators.
Hopefully this can get you started:
// "I have an array of observables..."
let one = Observable.deferred { Observable.just(1) }
let two = Observable.deferred { Observable.just(2) }
let observables = [one, two]
// "the output of each, should be the input of the next"
// this is problematic, because observables do not strictly have inputs.
let resultsFromEach = Observable.concat(observables)
resultsFromEach
.reduce(0) { result, next in
result + 1
}
.debug("result")
.subscribe()

Related

How to combine 2 publishers and erase values to Void?

I have 2 publishers where I want to perform an action based on either response. I don't care about the values. I'm trying to do something like this:
var hasChangedPublisher: AnyPublisher<(Void, Void), Never> {
Publishers.CombineLatest(
preferences.publisher,
state.$permissionStatus
).eraseToAnyPublisher()
}
If preferences.publisher fires first but not the other, I want to fire. If state.$permissionStatus fires but not the other, I want to fire. I don't really want to CombineLatest, but not sure how to fire if either emit.
Is there a way to produce an even if either fire but more elegantly erase its values?
You're looking for Merge instead of CombineLatest. Your code for this would look a bit like the following:
var hasChangedPublisher: AnyPublisher<Void, Never> {
preferences.publisher
.merge(state.$permissionStatus)
.map({ _ in
return () // transform to Void
})
.eraseToAnyPublisher()
}
Instead of CombineLatest, use Merge.
CombineLatest creates a tuple based on all the publishers that are combined. It is great, except, it will not fire at all until every one of the combined publishers has fired at least once. After that, it will fire once for every firing of any of its combined publishers - (merging the last value from each publisher into the tuple).
Merge, instead, just multiplexes all combined publishers together and generates a stream of events - containing a stream of single values from any of the combined publishers.
// transform to Void
.map { _ in }

How to handle a multiple singles?

I'm writing messenger server on grpc with rxjava2 stubs, and I stuck on combining my singles.
I'm have tried some sort of
val user:Single<User> = getUser()
val conversation:Single<Conversation> = getConversation(user.blockingGet())
return conversation.map{
someMethod(user.blockingGet(), conversation.it())
it
}
It looks so unbeauty then all of the examples, so is there a way to combine all of this singles to one line?
First a small comment, usually you don't want to use blockingGet. Instead you use other combinators to compose your solution and in the end you use subscribe to evaluate it.
I'm assuming you want to combine multiple calls that return a Single where the result of the next call depends on the previous.
The combinator you are looking for is flatMap.
val user: Single<User> = getUser()
val singleOfSomething: Single<Conversation> = user.flatMap { user->
getConversation(user).flatMap {conversation ->
someMethod(user, conversation)
}
}
here the return type would be Single of whatever someMethod returns.
You would use subscribe to get that value out when you need it.

RxSwift, why using .never() not for testing

I am going through the tutorial:
https://marcosantadev.com/mvvmc-with-swift/
Which talks about MVVM-C design pattern. I have real trouble understanding of how and why .never() observable is used there (and in general why we would want to use .never() besides testing timeouts).
Could anyone give a reasonable example of .never() observable usage in swift code (not in testing) and explain why it is necessary and what are the alternatives?
I address all the actions from View to ViewModel. User taps on a button? Good, the signal is delivered to a ViewModel. That is why I have multiple input observables in ViewModel. And all the observables are optional. They are optional because sometimes I write tests and don't really want to provide all the fake observables to test some single function. So, I provide other observables as nil. But working with nil is not very convenient, so I provide some default behavior for all the optional observables like this:
private extension ViewModel {
func observableNavigation() -> Observable<Navigation.Button> {
return viewOutputFactory().observableNavigation ?? Observable.never()
}
func observableViewState() -> Observable<ViewState> {
return viewOutputFactory().observableViewState ?? Observable.just(.didAppear)
}
}
As you can see, if I pass nil for observableViewState I substitute it with just(.didAppear) because the ViewModel logic heavily depends on the state of view. On the other hand if I pass nil for observableNavigation I provide never() because I assume that non of the navigation button will ever be triggered.
But this whole story is just my point of view. I bet you will find your own place to use this never operator.
Maybe your ViewModel has different configurations (or you have different viewModel under the same protocol), one of which does not need to send any updates to its observers. Instead of saying that the observable does not exist for this particular case (which you would implement as an optional), you might want to be able to define an observable as a .never(). This is in my opinion cleaner.
Disclaimer - I am not a user of RxSwift, but I am assuming never is similar than in ReactiveSwift, i.e. a signal that never sends any value.
It's an open ended question, and there can be many answers, but I've found myself reaching for never on a number of cases. There are many ways to solve a problem, but recently, I was simplifying some device connection code that had a cascading fail over, and I wanted to determine if my last attempt to scan for devices yielded any results.
To do that, I wanted to create an observable that only emitted a "no scan results" event in the event that it was disposed without having seen any results, and conversely, emitted nothing if it did.
I have pruned out other details from my code to sake of brevity, but in essence:
func connect(scanDuration: TimeInterval) -> Observable<ConnectionEvent> {
let scan = scan(for: scanDuration).share(replay: 1)
let connection: Observable<ConnectionEvent> =
Observable.concat(Observable.from(restorables ?? []),
connectedPeripherals(),
scan)
.flatMapLatest { [retainedSelf = self] in retainedSelf.connect(to: $0) }
let scanDetector = scan
.toArray() // <-- sum all results as an array for final count
.asObservable()
.flatMap { results -> Observable<ConnectionEvent> in
results.isEmpty // if no scan results
? Observable.just(.noDevicesAvailable) // emit event
: Observable.never() } // else, got results, no action needed
// fold source and stream detector into common observable
return Observable.from([
connection
.filter { $0.isConnected }
.flatMapLatest { [retained = self] event -> Observable<ConnectionEvent> in
retained.didDisconnect(peripheral: event.connectedPeripheral!.peripheral)
.startWith(event) },
scanDetector])
.switchLatest()
}
For a counter point, I realized as I typed this up, that there is still a simpler way to achieve my needs, and that is to add a final error emitting observable into my concat, it fails-over until it hits the final error case, so I don't need the later error detection stream.
Observable.concat(Observable.from(restorables ?? []),
connectedPeripherals(),
scan,
hardFailureEmitNoScanResults())
That said, there are many cases where we may want to listen and filter down stream, where the concat technique is not available.

How to map over an array, use an if clause, and filter out the bad data?

I have an array of words, some may or may not have typos.
potentialWords = ["hello", "lkasjdf", "hunry"]
What I want to do is, return an array of all valid words, and also those words that were able to be autocorrected using a function I created correctWord. It returns an array of potential matches. so "hunry" might return ["hungry", "hurry"]. I will select the first index for the best guess.
Some words cannot be corrected however! e.g. "lkasjdf" will not find any corrections, but "hunry" will.
I was trying something like:
potentialWords.map {
if correctWord($0) != nil {
return correctWord($0)[0]
}
}
of course this will complain and say that I need a return outside the if clause. I can filter the list based on if the word can be corrected, and then map over the filtered list, re-checking which words need to be corrected, but this runs the correctWord function way too many times, and it is very sensitive.
I would like to be able to do one single pass through, and return an array of all valid words, and also corrected words.
P.S. I am calling correctWord twice in the map function for brevity, but of course I would assign correctWord($0) to a variable, and then if it isn't nil, take the first index and add it to the new list.
I think you're after flatMap. It's the same as map except it will also filter out any nil values.
potentialWords.flatMap { correctWord($0)?.first }

can i conditionally "merge" a Single with an Observable?

i'm a RxJava newcomer, and i'm having some trouble wrapping my head around how to do the following.
i'm using Retrofit to invoke a network request that returns me a Single<Foo>, which is the type i ultimately want to consume via my Subscriber instance (call it SingleFooSubscriber)
Foo has an internal property items typed as List<String>.
if Foo.items is not empty, i would like to invoke separate, concurrent network requests for each of its values. (the actual results of these requests are inconsequential for SingleFooSubscriber as the results will be cached externally).
SingleFooSubscriber.onComplete() should be invoked only when Foo and all Foo.items have been fetched.
fetchFooCall
.subscribeOn(Schedulers.io())
// Approach #1...
// the idea here would be to "merge" the results of both streams into a single
// reactive type, but i'm not sure how this would work given that the item emissions
// could be far greater than one. using zip here i don't think it would every
// complete.
.flatMap { foo ->
if(foo.items.isNotEmpty()) {
Observable.zip(
Observable.fromIterable(foo.items),
Observable.just(foo),
{ source1, source2 ->
// hmmmm...
}
).toSingle()
} else {
Single.just(foo)
}
}
// ...or Approach #2...
// i think this would result in the streams for Foo and items being handled sequentially,
// which is not really ideal because
// 1) i think it would entail nested streams (i get the feeling i should be using flatMap
// instead)
// 2) and i'm not sure SingleFooSubscriber.onComplete() would depend on the completion of
// the stream for items
.doOnSuccess { data ->
if(data.items.isNotEmpty()) {
// hmmmm...
}
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> /* onSuccess() */ },
{ error -> /* onError() */ }
)
any thoughts on how to approach this would be greatly appreciated!
bonus points: in trying to come up with a solution to this, i've begun to question the decision to use the Single reactive type vs the Observable reactive type. most (all, except this one Foo.items case?) of my streams actually revolve around consuming a single instance of something, so i leaned toward Single to represent my streams as i thought it would add some semantic clarity around the code. anybody have any general guidance around when to use one vs the other?
You need to nest flatMaps and then convert back to Single:
retrofit.getMainObject()
.flatMap(v ->
Flowable.fromIterable(v.items)
.flatMap(w ->
retrofit.getItem(w.id).doOnNext(x -> w.property = x)
)
.ignoreElements()
.toSingle(v)
)