private var cache = [String: Weather]()
override func viewDidLoad() {
let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text)
.do(onNext: { [weak self] data in
self?.cache[text] = data
})
.catchError { error in
return Observable.just(self.cache[text] ?? .empty)
}
}
let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text)
.do(onNext: { [weak self] data in
self?.cache[text] = data
})
.retry(3)
.catchError { [weak self] error in
return Observable.just(self?.cache[text] ?? .empty)
}
}
let maxAttempts = 4
let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text)
.do(onNext: { [weak self] data in
self?.cache[text] = data
}).retryWhen { e in
return e.enumerated().flatMap { attempt, error -> Observable<Int> in
print("== retrying after \(attempt + 1) seconds ==")
if attempt >= maxAttempts - 1 {
return Observable.error(error)
}
return Observable<Int>.timer(.seconds(attempt + 1),
scheduler: MainScheduler.instance)
.take(1)
}
}
.catchError { [weak self] error in
return Observable.just(self?.cache[text] ?? .empty)
}
}
== retrying after 1 seconds ==
... network ...
== retrying after 2 seconds ==
... network ...
== retrying after 3 seconds ==
... network ...
ApiController
private func buildRequest(method: String = "GET", pathComponent: String, params: [(String, String)]) -> Observable<Data> {
let request: Observable<URLRequest> = Observable.create { observer in
let url = self.baseURL.appendingPathComponent(pathComponent)
var request = URLRequest(url: url)
let keyQueryItem = URLQueryItem(name: "appid", value: try? self.apiKey.value())
let unitsQueryItem = URLQueryItem(name: "units", value: "metric")
let urlComponents = NSURLComponents(url: url, resolvingAgainstBaseURL: true)!
if method == "GET" {
var queryItems = params.map { URLQueryItem(name: $0.0, value: $0.1) }
queryItems.append(keyQueryItem)
queryItems.append(unitsQueryItem)
urlComponents.queryItems = queryItems
} else {
urlComponents.queryItems = [keyQueryItem, unitsQueryItem]
let jsonData = try! JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request.httpBody = jsonData
}
request.url = urlComponents.url!
request.httpMethod = method
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
observer.onNext(request)
observer.onCompleted()
return Disposables.create()
}
let session = URLSession.shared
return request.flatMap { request in
return session.rx.response(request: request)
.map { response, data in
switch response.statusCode {
case 200 ..< 300:
return data
case 400 ..< 500:
throw ApiError.cityNotFound
default:
throw ApiError.serverFailure
}
}
}
}
ViewController
let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text)
.do(onNext: { [weak self] data in
self?.cache[text] = data
},
onError: { error in
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.showError(error: error)
}
})
.catchError { [weak self] error in
return Observable.just(self?.cache[text] ?? .empty)
}
}
private func showError(error e: Error) {
guard let e = e as? ApiController.ApiError else {
InfoView.showIn(viewController: self, message: "An error occurred")
return
}
switch e {
case .cityNotFound:
InfoView.showIn(viewController: self, message: "City Name is invalid")
case .serverFailure:
InfoView.showIn(viewController: self, message: "Server error")
}
}