Home > Net >  Make a network call every 10 seconds with RxSwift
Make a network call every 10 seconds with RxSwift

Time:11-11

I am completely new to RxSwift. I managed to load my table view but now I would like to make a call every 10 seconds.

I was reading here that I should probably use Observable<Int>.interval(10, scheduler: MainScheduler.instance), I tried without much success.

class MarketService: MarketServiceProtocol  {
    func fetchMarkets() -> Observable <[Market]> {
        return Observable.create { observer -> Disposable in
            RestManager.shared.makeRequest(withEndPoint: "market/v2/get-summary?region=US" , withHttpMethod: .get) { result in
                if let error = result.error {
                    observer.onError(error)
                    return
                }
                guard let response = result.response,
                      200 ... 299 ~= response.httpStatusCode else {
                    return
                }
                guard let data = result.data else {
                    return
                }
                do {
                    let decodedData = try JSONDecoder().decode(MarketResult.self, from: data)
                    observer.onNext(decodedData.marketSummaryAndSparkResponse.markets)
                } catch {
                    observer.onError(error)
                }
            }
            return Disposables.create { }
        }
    }
}

then I call in my view controller:

viewModel.fetchMarketViewModels().observe(on: MainScheduler.instance).bind(to: tableView.rx.items(cellIdentifier: HomeTableViewCell.cellIdentifier)) {
        index, viewModel, cell in
        guard let cell = cell as? HomeTableViewCell else { return }
        cell.setupData(viewModel: viewModel)
    }.disposed(by: self.disposableBag)

CodePudding user response:

There are a couple of problems with your Observable.create closure. You have to make sure that something is sent to the observer in every path, otherwise the Observable will call the function and then not emit anything and you will not know why.

Also, you want to minimize the amount of logic being performed in the closure passed to create. You are doing way too much in there.

So let's simplify the code in the create closure as much as possible first:

extension RestManager {
    func rx_makeRequest(withEndPoint endPoint: String, withHttpMethod method: HttpMethod) -> Observable<(response: MyHTTPURLResponse, data: Data)> {
        Observable.create { observer in
            self.makeRequest(withEndPoint: endPoint, withHttpMethod: method) { result in
                if let response = result.response, let data = result.data {
                    observer.onNext((response, data))
                    observer.onCompleted()
                }
                else {
                    observer.onError(result.error ?? RxError.unknown)
                }
            }
            return Disposables.create() // is there some way of canceling a request? If so, it should be done here.
        }
    }
}

This does the bare minimum. Just wraps the underlying callback and nothing else. Now your fetchMarkets call is much simpler:

class MarketService: MarketServiceProtocol  {
    func fetchMarkets() -> Observable <[Market]> {
        return RestManager.shared.rx_makeRequest(withEndPoint: "market/v2/get-summary?region=US", withHttpMethod: .get)
            .do(onNext: { result in
                guard 200...299 ~= result.response.httpStatusCode
                else { throw URLError.httpRequestFailed(response: result.response, data: result.data) }
            })
            .map { try JSONDecoder().decode(MarketResult.self, from: $0.data).marketSummaryAndSparkResponse.markets }
    }
}

Now to the meat of your question. How to make the network call every 10 seconds... Just wrap your network call in a flatMap like this:

Observable<Int>.interval(.seconds(10), scheduler: MainScheduler.instance)
    .flatMapLatest { _ in
        viewModel.fetchMarketViewModels()
    }
    .observe(on: MainScheduler.instance)
    .bind(to: tableView.rx.items(cellIdentifier: HomeTableViewCell.cellIdentifier)) { index, viewModel, cell in
        guard let cell = cell as? HomeTableViewCell else { return }
        cell.setupData(viewModel: viewModel)
    }
    .disposed(by: self.disposableBag)

Learn more about flatMap and its variants from this article.

  • Related