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.