ReactiveX RxSwift get first non error from concat of observables - ios

I am using RxSwift for caching in my iOS app and have a piece of code like this:
let observable = Observable.of(cache.getItem(itemID), network.getItem(itemID)).concat().take(1)
observable.subscribeNext // and do some stuff
I have the cache.getItem method doing an onError if it has no value, and would like it to then defer to the network, but for some reason the network is never run. I assume its because I am using the take(1), but I would like the observable to stop emitting once the cache finds something (or continue to the network if it does not).
Any ideas on how to do this?
I've been following this guide but he does not go into detail about his cache's behavior when it fails to find something.

You shouldn't be using .Error like that. That's not really conceptually an error case. There's just nothing in the cache. That's a common situation. Nothing went "wrong" out of the ordinary. Instead, just send a .Completed event.
As for why your code isn't working, it's because an error coming from an Observable included in the concat will become an error on the final concat Observable. The thing to remember with Rx is that once there's a .Completed event or (in your case) an .Error event, that's it, it's over, no more .Next events (or any events).
So instead, if you use .Completed, your code would work as so:
class Cache {
func getItem(itemID: Int) -> Observable<Item> {
return Observable<Item>.create { observer in
// if not found...
observer.onCompleted() // you would of course really try to get it
// from the cache first.
return NopDisposable.instance
}
}
}
class Network {
func getItemN(itemID: Int) -> Observable<Item> {
return Observable<Item>.create { observer in
// get some `item` from the network and then..
observer.onNext(item)
return NopDisposable.instance
}
}
}
let observable = Observable.of(cache.getItem(itemID), network.getItem(itemID)).concat().take(1)
observable.subscribeNext { item in
print(item)
}

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/

How can I transform a signal with errors into a NoError one with ReactiveSwift? (and be elegant)

What is the most elegant way to transform my ReactiveSwift's SignalProducer<A, NetworkError> into a Signal<A, NoError>?
Most of the time, my signal producer is the result of a network call, so I want to split the results into two cases:
if a value is available, send a Signal<A, NoError>
if an error happened, send a Signal<String, NoError> with the error's localized description
(why? because i'm trying to be as MVVM as possible)
So far, I end up writing a lot of boilerplate like the following:
let resultsProperty = MutableProperty<SearchResults?>(nil)
let alertMessageProperty = MutableProperty<String?>(nil)
let results = resultsProperty.signal // `Signal<SearchResults?, NoError>`
let alertMessage = alertMessageProperty.signal // `Signal<String?, NoError>`
// ...
searchStrings.flatMap(.latest) { string -> SignalProducer<SearchResults, NetworkError> in
return MyService.search(string)
}
.observe { event in
switch event {
case let .value(results):
resultsProperty.value = results
case let .failed(error):
alertMessageProperty.value = error
case .completed, .interrupted:
break
}
}
ie:
using MutableProperty instances, that I have to set as optional to be able to initialize them
creating signals from those, ie getting a signal sending optionals as well
it feels dirty and makes the code so intertwined it kind of ruins the point of being reactive
Any help on (A) keeping my signals non optional and (B) splitting them into 2 NoError signals elegantly would be greatly appreciated.
Edit - Second Attempt
I will try to answer all your questions / comments here.
The errors = part doesn't work as flatMapError expects a SignalProducer (ie your sample code works just because searchStrings is a Signal string, which coincidently is the same as the one we want for errors: it does not work for any other kind of input)
You are correct, this is because flatMapError does not change the value type. (Its signature is func flatMapError<F>(_ transform: #escaping (Error) -> SignalProducer<Value, F>) -> SignalProducer<Value, F>). You could add another call to map after this if you need to change it into another value type.
the results = part behaves weirdly as it terminates the signal as soon as an error is met (which is a behavior I don't want) in my real-life scenario
Yes, this is because the flatMap(.latest) forwards all errors to the outer signal, and any error on the outer signal will terminate it.
Okay so here's an updated version of the code, with the extra requirements that
errors should have different type than searchStrings, let's say Int
Any error from MyService.search($0) will not terminate the flow
I think the easiest way to tackle both these issues is with the use of materialize(). What it does is basically "wrap" all signal events (new value, error, termination) into a Event object, and then forward this object in the signal. So it will transform a signal of type Signal<A, Error> into a Signal<Event<A, Error>, NoError> (you can see that the returned signal does not have an error anymore, since it is wrapped in the Event).
What it means in our case is that you can use that to easily prevent signals from terminating after emitting errors. If the error is wrapped inside an Event, then it will not automatically terminate the signal who sends it. (Actually, only the signal calling materialize() completes, but we will wrap it inside the flatMap so the outer one should not complete.)
Here's how it looks like:
// Again, I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
// Except this time, we wrap the events with `materialize()`
return MyService.search($0).materialize()
}
// Now Since `searchResults` is already `NoError` you can simply
// use `filterMap` to filter out the events that are not `.value`
results = searchResults.filterMap { (event) in
// `event.value` will return `nil` for all `Event`
// except `.value(T)` where it returns the wrapped value
return event.value
}
// Same thing for errors
errors = searchResults.filterMap { (event) in
// `event.error` will return `nil` for all `Event`
// except `.failure(Error)` where it returns the wrapped error
// Here I use `underestimatedCount` to have a mapping to Int
return event.error?.map { (error) in
// Whatever your error mapping is, you can return any type here
error.localizedDescription.characters.count
}
}
Let me know if that helps! I actually think it looks better than the first attempt :)
First Attempt
Do you need to access the state of you viewModel or are you trying to go full state-less? If state-less, you don't need any properties, and you can just do
// I assume this is what you get from the user
let searchStrings: Signal<String, NoError>
// Keep your flatMap
let searchResults = searchStrings.flatMap(.latest) {
return MyService.search($0)
}
// Use flatMapError to remove the error for the values
results = searchResults.flatMapError { .empty }
// Use flatMap to remove the values and keep the errors
errors = searchResults.filter { true }.flatMapError { (error) in
// Whatever you mapping from error to string is, put it inside
// a SignalProducer(value:)
return SignalProducer(value: error.localizedDescription)
}

Chaining RxSwift observable with different type

I need request different types of models from network and then combine them into one model.
How is it possible to chain multiple observables and return another observable?
I have something like:
func fetchDevices() -> Observable<DataResponse<[DeviceModel]>>
func fetchRooms() -> Observable<DataResponse<[RoomModel]>>
func fetchSections() -> Observable<DataResponse<[SectionModel]>>
and I need to do something like:
func fetchAll() -> Observable<(AllModels, Error)> {
fetchSections()
// Then if sections is ok I need to fetch rooms
fetchRooms()
// Then - fetch devices
fetchDevices()
// And if everything is ok create AllModels class and return it
// Or return error if any request fails
return AllModels(sections: sections, rooms: rooms, devices:devices)
}
How to achieve it with RxSwift? I read docs and examples but understand how to chain observables with same type
Try combineLatest operator. You can combine multiple observables:
let data = Observable.combineLatest(fetchDevices, fetchRooms, fetchSections)
{ devices, rooms, sections in
return AllModels(sections: sections, rooms: rooms, devices:devices)
}
.distinctUntilChanged()
.shareReplay(1)
And then, you subscribe to it:
data.subscribe(onNext: {models in
// do something with your AllModels object
})
.disposed(by: bag)
I think the methods that fetching models should reside in ViewModel, and an event should be waiting for start calling them altogether, or they won't start running.
Assume that there's a button calls your three methods, and one more button that will be enabled if the function call is succeeded.
Consider an ViewModel inside your ViewController.
let viewModel = ViewModel()
In ViewModel, declare your abstracted I/O event like this,
struct Input {
buttonTap: Driver<Void>
}
struct Output {
canProcessNext: Driver<Bool>
}
Then you can clearly transform your Input into Output by making function like this in ViewModel.
func transform(input: Input) -> Output {
// TODO: transform your button tap event into fetch result.
}
At viewDidLoad,
let output = viewModel.transform(input: yourButton.rx.tap.asDriver())
output.drive(nextButton.rx.isEnabled).disposed(by: disposeBag)
Now everything's ready but combining your three methods - put them in ViewModel.
func fetchDevices() -> Observable<DataResponse<[DeviceModel]>>
func fetchRooms() -> Observable<DataResponse<[RoomModel]>>
func fetchSections() -> Observable<DataResponse<[SectionModel]>>
Let's finish the 'TODO'
let result = input.buttonTap.withLatestFrom(
Observable.combineLatest(fetchDevices(), fetchRooms(), fetchSections()) { devices, rooms, sections in
// do your job with response data and refine final result to continue
return result
}.asDriver(onErrorJustReturn: true))
return Output(canProcessNext: result)
I'm not only writing about just make it work, but also considering whole design for your application. Putting everything inside ViewController is not a way to go, especially using Rx design. I think it's a good choice to dividing VC & ViewModel login for future maintenance. Take a look for this sample, I think it might help you.

Proper way to dispose a one-off observable in RxSwift

I have an observable that I only want to kick off once. The docs say:
Using dispose bags or takeUntil operator is a robust way of making sure resources are cleaned up. We recommend using them in production even if the sequences will terminate in finite time.
My observable terminates after just one event
let observable = Observable.create() { observer in
webservice.makeHTTPRequestWithCompletionBlock {
if something {
observer.on(.Next(...))
observer.onCompleted()
} else {
observer.on(.Error(...))
}
}
}
Say I wasn't interested in cancelling subscribers to this observable, I just want it run once and complete. I want the lifecycle of this observable to end when the work itself is completed. Meaning there are no good candidates for disposeBag that I can see. takeUntil also expects an 'event', and there are no good ones that I can see.
Right now I just solve the warning by throwing away the disposable:
_ = observeable.subscribeNext { ... }
Is there a way to do this, or a different paradigm that I should use?
Both DiposeBag and takeUntil are used to cancel a subscription prior to receiving the .Completed/.Error event.
When an Observable completes, all the resources used to manage subscription are disposed of automatically.
As of RxSwift 2.2, You can witness an example of implementation for this behavior in AnonymousObservable.swift
func on(event: Event<E>) {
switch event {
case .Next:
if _isStopped == 1 {
return
}
forwardOn(event)
case .Error, .Completed:
if AtomicCompareAndSwap(0, 1, &_isStopped) {
forwardOn(event)
dispose()
}
}
}
See how AnonymousObservableSink calls dispose on itself when receiving either an .Error or a .Completed event, after forwarding the event.
In conclusion, for this use case, _ = is the way to go.

Error Handling - Async Call

I am creating a framework for web services used in my project. I have uploaded template in GitHub. https://github.com/vivinjeganathan/ErrorHandling
It has various layers. Layer 1 for validation. Layer 2 for formation of request. Layer 3 for the actual network call.
View Controller <----> Layer 1 <---> Layer 2 <---> Layer 3
Data flows between layers through closures, if error happens at any layer it needs to be gracefully passed to the ViewController.
I have referred to this link for error handling in async calls - http://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/
Created a branch in the same repo - name - ErrorHandling-Method1.
I was able to transfer error from layer 3 to layer 2(Single Level - Returning response through functions in closures - as mentioned in the link). But face difficulties in transferring back across multi layers.
Can anyone assist with the sample application provided in public GitHub?
First of all, I don't think it's necessary to stack the layers the way you did, for example, by adding the validation functionality as a layer you are increasing coupling making that layer dependant of the layers below (parsing, networking, etc.), instead, why don't you separate validation to make it only dependant of the data?:
class ViewController: UIViewController {
var validator = InputValidator()
override func viewDidLoad() {
super.viewDidLoad()
do {
try validator.validateInput("INPUT")
try Fetcher.requestDataWithParams("INPUT")
}
catch {
handleError(error)
}
}
}
Now the validation functionality is not dependant of the other layers, so communication would flow like this:
View Controller <---> ParsingLayer <---> NetworkingLayer
I did rename the layers but they are not necessarily have to be like this, you can add or remove layers.
I think is going to be kind of complicated if I try to explain my approach, so I'm going to give an example using the previous layers, first the bottom layer:
class NetworkingLayer {
class func requestData(params: AnyObject, completion: (getResult: () throw -> AnyObject) -> Void) -> Void {
session.dataTaskWithURL(url) { (data, urlResponse, var error) in
if let error = error {
completion(getResult: { throw error })
} else {
completion(getResult: { return data })
}
}
}
}
I have omitted some sections of code, but the idea is to do any necessary step to make the layer work (create session, etc.) and to always communicate back through the completion closure; a layer on top would look like this:
class ParsingLayer {
class func requestObject(params: AnyObject, completion: (getObject: () throw -> CustomObject) -> Void) -> Void {
NetworkingLayer.requestData(params, completion: { (getResult) -> Void in
do {
let data = try getResult()
let object = try self.parseData(data)
completion(getObject: { return object })
}
catch {
completion(getObject: { throw error })
}
})
}
}
Notice that the completion closures are not the same, since every layer adds functionality, the returned object can change, also notice that the code inside the do statement can fail in two ways, first if the network call fails and then if the data from the networking layer cannot be parsed; again the communication to the layer on top is always done through the completion closure.
Finally the ViewController can call the next layer using the closure expected by the Parsing layer in this case, and is able to handle errors originated in any layer:
override func viewDidLoad() {
super.viewDidLoad()
do {
try validator.validateInput("INPUT")
try ParsingLayer.requestObject("INPUT", completion: { (getObject) in
do {
let object = try getObject()
try self.validator.validateOutput(object)
print(object)
}
catch {
self.handleError(error)
}
})
catch {
handleError(error)
}
}
Notice that there is a do catch inside the completion closure, this is necessary since the call is made asynchronously, now that the response has gone through all the layers and have actually change to be of a more specialised type you can even validate the result without having the necessity to make a layer for the validation functionality.
Hope it helps.
Personally I would use notifications passing the NSError as the object of the notification in the layers and observe the notification in the view controller.
In the layers:
NSNotificationCenter.defaultCenter().postNotificationName("ErrorEncounteredNotification", object: error)
In the view controller
NSNotificationCenter.defaultCenter().addObserver(self, selector: "errorEncountered:", name: "ErrorEncounteredNotification", object: nil)
the selector method:
func errorEncountered(notification: NSNotification!) {
let error: NSError! = notification.object as! NSError
NSLog("error: \(error)")
}
You correctly identified a nasty problem with error handling in asynchronous code.
It seems to be easy with synchronous functions - which just return an error code, or have an extra error parameter, or use the new Swift throws syntax. Here is an synchronous function:
func computeSome() throws -> Some
And this is a viable function signature for an asynchronous function:
func computeSomeAsync(completion: (Some?, NSError?) -> ())
The asynchronous function returns Void and does not throw. If it fails, it calls its completion function with the error parameter set.
However, completion handlers become quickly cumbersome, especially in nested code.
The solution is to use a Future:
func computeSomeAsync() -> Future<Some>
This function is asynchronous and does not throw - and returns a Future. So, what's a future?
Well a future represents the eventual result of an asynchronous function. When you call the asynchronous function, it immediately returns and you get a placeholder for the result. This, called a future, will be eventually completed by the underlying background task that computes the value.
When the underlying task eventually succeeded, this future contains the computed value of the function. When it failed it will contain the error.
Depending on the actual implementation and the API of a Future Library, you can obtain the result by registering continuations:
let future = computeSomeAsync()
future.onSuccess { value in
print("Value: \(value)")
}
future.onFailure { error in
print("Error: \(error)")
}
It may look weird at first, but you can do awesome things with futures:
fetchUser(id).flatMap { user in
fetchProfileImage(user.profileImageUrl).flatMap { image in
cacheImage(image)
}
}
.onFailure { error in
print("Something went wrong: \(error)")
}
The above statement is asynchronous - as well as function fetchUser, fetchProfileImage and cacheImage. Error handling included.
Why declare your method throws if you never throw or even try to catch? You could throw the errors using the throwable declaration through all the layers, and even change the throwable type at each level.
UPDATE: Didnt think of throwing dont work in async operations. Using NSNotification is one good route, or you could take a look at RXSwift or similar to solve it too. My personal recommendation would be to use RxSwift. This keeps you out of callback hell, which you are currently travelling down into.

Resources