swiftrx-swiftmoya

Run multiple request at a time and continue as soon there is success


Given an array of urls, is there a way to run those at once simultaneously? But in such a way so as soon as there is one success move to the next request using that successful url.

So far I tried chaining concatMap, and failed with zip.

func updateAccountInfo() -> Single<Bool> {
    
    var disposable: Disposable? = nil
    
    return Single<Bool>.create { observer in
      
      do {
        
        var urls = try self.settings.domains.value()
        
        disposable = Observable.from(urls)
          .enumerated()
          .concatMap { index, url -> Single<URL> in
            return self.verifyUrl(url)
          }
          .concatMap { url -> Single<Account> in
            return self.apiManager.loadAccountInfo(from: url)
          }
          .observeOn(MainScheduler.instance)
          .do(onNext: { (account: AccountInfo) in
            // use account unfo here
            disposable!.dispose()
          })
          .subscribe()
        
      } catch {
        observer(.error(error))
      }
      
      return Disposables.create()
    }
  }

Tried like so too:

disposable = Observable.from(urls)
          .enumerated()
          .concatMap { index, url -> Single<(Bool, URL)> in
            return self.verifyUrl(url)
          }
          .subscribe(onNext: { reachable, url in
            if reachable {

              self.apiManager.loadAccountInfo(from: url)
                .subscribe(onSuccess: { accountInfo in
                  
                  // use account info here

                }, onError: { error in
                  
                })
                .disposed(by: self.bag)

              disposable!.dispose()
            } else {
              
            }
          }, onError: { error in
            
          }, onCompleted: {
          
          })

Maybe I use zip but how would I create an array of verifyUrl(url) calls? Does zip accept arrays of Observable at all?

let obs = Observable.from(urls)
          .enumerated()
          .concatMap { index, url -> Single<URL> in
            return self.verifyUrl(url)
          }
        
        let test = Observable
        .zip(obs).map { [urls] in 
          return [urls]
        }

Solution

  • If I understand the question correctly, you are looking for something like this:

    func example() throws {
        let urls = try self.settings.domains.value()
        Observable.merge(urls.map { verifyUrl($0).asObservable() })
            .flatMap { [apiManager] url in
                apiManager!.loadAccountInfo(from: url)
            }
            .observe(on: MainScheduler.instance)
            .subscribe(onNext: { account in
                // use account unfo here
            })
            .disposed(by: self.disposeBag)
    }
    

    But it's hard to tell. Your code samples are a bit jumbled. Putting all your code in a Single.create is odd. Returning a Disposables.create() from the closure when you have a disposable to return is odd. Calling dispose() on a disposable inside the do block is odd. So much weirdness... I suggest you post some code on https://codereview.stackexchange.com or look at sample code.