Group Group Group Group Group Group Group Group Group

Chapter 12 - Improve Challenge Solution

Hi,

The solution for chapter 12’s challenge is:

let textSearch = searchCityName.rx.controlEvent(.editingDidEndOnExit).asObservable()
let temperature = tempSwitch.rx.controlEvent(.valueChanged).asObservable()

let search = Observable.from([textSearch, temperature])
  .merge()
  .map { self.searchCityName.text }
  .filter { ($0 ?? "").characters.count > 0 }
  .flatMap { text in
    return ApiController.shared.currentWeather(city: text ?? "Error")
      .catchErrorJustReturn(ApiController.Weather.empty)
  }.asDriver(onErrorJustReturn: ApiController.Weather.empty)

search.map { w in
  if self.tempSwitch.isOn {
    return "\(Int(Double(w.temperature) * 1.8 + 32))° F"
  }
  return "\(w.temperature)° C"
  }
  .drive(tempLabel.rx.text)
  .addDisposableTo(bag)

Though it works, I find it bothering that every time the switch is flipped, a new (useless) request is made to the API.

One solution I found is:

let textSearch = searchCityName.rx.controlEvent(.editingDidEndOnExit).asObservable()
let temperature = tempSwitch.rx.controlEvent(.valueChanged).asObservable()

let search = textSearch
  .map { self.searchCityName.text }
  .filter { ($0 ?? "").characters.count > 0 }
  .flatMapLatest { text in
    return ApiController.shared.currentWeather(city: text ?? "Error")
  }

let searchDrive = search.asDriver(onErrorJustReturn: ApiController.Weather.empty)

Observable.combineLatest(search, temperature) { $0.0 }
  .map { w in
    if self.tempSwitch.isOn {
      return "\(w.temperature)° C"
    } else {
      return "\(Int(Double(w.temperature) * 1.8 + 32))° F"
    }
  }
  .bind(to: tempLabel.rx.text)
  .addDisposableTo(bag)

It works well but I’m losing the niceties of using a Driver for the temperature label.

I’d love some insights on how to solve that challenge using only Drivers and not doing extraneous api requests.

Thanks,

1 Like

My solution using a switch:

    Driver.combineLatest(search, fahrenheitSwitch.rx.isOn.asDriver()) { data, isFahrenheit -> String in
        if isFahrenheit {
            return "\(data.temperatureF)° F"
        } else {
            return "\(data.temperature)° C"
        }
        }.drive(tempLabel.rx.text)
        .disposed(by: bag)
2 Likes

I had exactly the same conclusion :slight_smile: , that going to a back-end after switching C/F is unnecessary.

Similar solution to @aivcec

    Observable.combineLatest(search.asObservable(), convertToFahrenheitSwitch.rx.isOn) {
        searchResult, shouldConvertToFahrenheit in
        var temperature = searchResult.temperature
        var degrees = "C"
        if shouldConvertToFahrenheit {
            temperature = self.convertToFahrenheit(inCelsius: temperature)
            degrees = "F"
        }
        return "\(temperature)° \(degrees)"
    }
    .bind(to: tempLabel.rx.text)
    .disposed(by: bag)

private func convertToFahrenheit(inCelsius: Int) -> Int {
    Int(Double(inCelsius) * 1.8) + 32
}