title: RxSwift observable error stops chain - Web services with Rx, how to recover?
body: <p>obviously I am new to RxSwift and though I consumed a lot of documentations and speeches, I think I am missing some fundamental concepts. </p>
  
  <p>In my app I have a RESTful web service to load various resources but the base url of the web service is unknown at build/start time. Instead I have a "URL resolver" web service which I can call with my apps bundle, version and possible environment ("production", "debug" or any custom string entered in the apps debug settings) to obtain the base url I then use for the actual service. </p>
  
  <p>My thinking was that I would create 2 services, one for the URL resolver and one for the actual web service which gives me my resources. The URL resolver would have a Variable and a Observable. I use the variable to signal the need to refresh the base url via a web service call to the URL resolver. I do this by observing the variable and filter only for true values. A function in the service class set the variables value to true (initially it is false) and inside an observer of the filtered variable, I make the web service call in another Observable (this example uses a dummy JSON web service):</p>
  
  <pre><code>import Foundation
  import RxSwift
  import Alamofire
  
  struct BaseURL: Codable {
      let title: String
  }
  
  struct URLService {
      private static var counter = 0
      private static let urlVariable: Variable&lt;Bool&gt; = Variable(false)
      static let urlObservable: Observable&lt;BaseURL&gt; = urlVariable.asObservable()
          .filter { counter += 1; return $0 }
          .flatMap { _ in
              return Observable.create { observer in
                  let url = counter &lt; 5 ? "https://jsonplaceholder.typicode.com/posts" : ""
                  let requestReference = Alamofire.request(url).responseJSON { response in
                      do {
                          let items = try JSONDecoder().decode([BaseURL].self, from: response.data!)
                          observer.onNext(items[0])
                      } catch {
                          observer.onError(error)
                      }
                  }
  
                  return Disposables.create() {
                      requestReference.cancel()
                  }
              }
      }
  
      static func getBaseUrl() {
          urlVariable.value = true;
      }
  
      static func reset() {
          counter = 0;
      }
  }
  </code></pre>
  
  <p>Now the problem is that sometimes it can happen that a web service call fails and I would need to show the error to the user so a retry can be made. I thought that the onError was useful for this but it seems to kills all the subscribers forever. </p>
  
  <p>I could put the subscribing in its own function and inside the error handler of the Observer, I could show a alert and then call the subscribe function again like so: </p>
  
  <pre><code>func subscribe() {
          URLService.urlObservable.subscribe(onNext: { (baseURL) in
              let alert = UIAlertController(title: "Success in Web Service", message: "Base URL is \(baseURL.title)", preferredStyle: .alert)
              let actionYes = UIAlertAction(title: "Try again!", style: .default, handler: { action in
                  URLService.getBaseUrl()
              })
              alert.addAction(actionYes)
              DispatchQueue.main.async {
                  let alertWindow = UIWindow(frame: UIScreen.main.bounds)
                  alertWindow.rootViewController = UIViewController()
                  alertWindow.windowLevel = UIWindowLevelAlert + 1;
                  alertWindow.makeKeyAndVisible()
                  alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
              }
          }, onError: { error in
              let alert = UIAlertController(title: "Error in Web Service", message: "Something went wrong: \(error.localizedDescription)", preferredStyle: .alert)
              let actionYes = UIAlertAction(title: "Yes", style: .default, handler: { action in
                  URLService.reset()
                  self.subscribe()
              })
              alert.addAction(actionYes)
              DispatchQueue.main.async {
                  VesselService.reset()
                  let alertWindow = UIWindow(frame: UIScreen.main.bounds)
                  alertWindow.rootViewController = UIViewController()
                  alertWindow.windowLevel = UIWindowLevelAlert + 1;
                  alertWindow.makeKeyAndVisible()
                  alertWindow.rootViewController?.present(alert, animated: true, completion: nil)
              }
          }).disposed(by: disposeBag)
      }
  </code></pre>
  
  <p>Then in my AppDelegate I would call </p>
  
  <pre><code>subscribe()
  URLService.getBaseUrl()
  </code></pre>
  
  <p>The problem is that all other observers get killed on an error as well but since the the only other observer on the URLService.urlObservable is my other web service class, I guess I could implement the same style subscribe function in there as well. </p>
  
  <p>I read that some people suggest to return a Result enum which has 2 cases: the actual result (.success(result: T)) or an error (.error(error: Error)).</p>
  
  <p>So what is the better way of handling errors web service errors in Rx? I cant wrap my head around this problem and I'm trying for 2 days to understand it. Any ideas or suggestions? </p>
  
  <p><strong>Update</strong></p>
  
  <p>It just came to my mind that I could ignore errors from the web service calls completely and instead post any error to a global "error" variable which my app delegate could observe to show alerts. The "error" could reference the function which initially caused it so a retry could be made. I'm still confused and not sure what I should do. :/ </p>
  
  <p><strong>Update 2</strong></p>
  
  <p>I think I might found a working solution. As I am still a beginner to Rx and RxSwift, I'm happy to take improvement suggestions. As I was writing the actual code, I splitted my call chain in two parts:</p>
  
  <ul>
  <li>The part where I make the web service calls</li>
  <li>The part where I click a button and process the result of the web service, whether it is an error or a success</li>
  </ul>
  
  <p>In the part where I click the button and process the result, I use catchError and retry as suggested in the comments. The code looks like this:</p>
  
  <pre><code>let userObservable = URLService
      .getBaseUrl(environment: UserDefaults.standard.environment) //Get base url from web service 1
      .flatMap({ [unowned self] baseURL -&gt; Observable&lt;User&gt; in
          UserService.getUser(baseURL: baseURL,
                              email: self.usernameTextField.text!,
                              password: self.passwordTextField.text!) //Get user from web service 2 using the base url from webservice 1
      })
  
  
  signInButton
      .rx
      .tap
      .throttle(0.5, scheduler: MainScheduler.instance)
      .flatMap({ [unowned self] () -&gt; Observable&lt;()&gt; in
          Observable.create { observable in
              let hud = MBProgressHUD.present(withTitle: "Signing in...");
              self.hud = hud
              observable.onNext(())
              return Disposables.create {
                  hud?.dismiss()
              }
          }
      })
      .flatMap({ () -&gt; Observable&lt;User&gt; in
          return userObservable
      })
      .catchError({ [unowned self] error -&gt; Observable&lt;User&gt; in
          self.hud?.dismiss()
          self.handleError(error)
          return userObservable
      })
      .retry()
      .subscribe(onNext: { [unowned self] (user) in
          UserDefaults.standard.accessToken = user.accessToken
          UserDefaults.standard.tokenType = user.tokenType
          self.hud?.dismiss()
      })
      .disposed(by: disposeBag)
  </code></pre>
  
  <p>The trick was to move the call to the two web services out of the cain into their own variable so I can re-call it at any time. When I now return the "userObservable" and an error happens during the web service call, I can show the error in the catchError and return the same "userObservable" for the next retry. </p>
  
  <p>At the moment this only properly handles errors when they occur in the web service call chain so I think I should make the button tap a driver. </p>