RxSwift Ⅲ:iOS 应用程序与 RxCocoa

由于 Rx 是一个多平台框架,因此它不会对您的 Rx 驱动的应用程序运行的设备做出任何假设。 RxSwift 严格遵循 RxPython,RxRuby,RxJS 和所有其他平台所遵循的通用 API 设计,因此它不包含任何特定功能或与 UIKit 或 Cocoa 的集成,以帮助您开发 iOS 或 macOS。

RxCocoa 是一个独立的库(尽管它与 RxSwift 捆绑在一起),它允许您使用许多预构建的功能来更好地与 UIKit 和 Cocoa 集成。

RxCocoa 将为您提供开箱即用的类来进行反应式网络,对用户交互作出反应,将数据模型绑定到 UI 控件等等。

第十二章:开始学习 RxCocoa

更新天气信息界面,根据 API 获取的数据。

ApiController.shared.currentWeather(city: "RxSwift")
.observeOn(MainScheduler.instance)
.subscribe(onNext: { data in
self.tempLabel.text = "\(data.temperature)° C"
self.iconLabel.text = data.icon
self.humidityLabel.text = "\(data.humidity)%"
self.cityNameLabel.text = data.cityName
})
.disposed(by:bag)

let search = searchCityName.rx.controlEvent(.editingDidEndOnExit).asObservable()
.map { self.searchCityName.text }
.filter { ($0 ?? "").characters.count > 0 }
.flatMapLatest { text in
return ApiController.shared.currentWeather(city: text ?? "Error")
.catchErrorJustReturn(ApiController.Weather.empty)
}
.asDriver(onErrorJustReturn: ApiController.Weather.empty)

search.map { "\($0.temperature)° C" }
.drive(tempLabel.rx.text)
.disposed(by:bag)

search.map { $0.icon }
.drive(iconLabel.rx.text)
.disposed(by:bag)

search.map { "\($0.humidity)%" }
.drive(humidityLabel.rx.text)
.disposed(by:bag)

search.map { $0.cityName }
.drive(cityNameLabel.rx.text)
.disposed(by:bag)

第十三章:RxCocoa 中级

添加搜索框支持

override func viewDidLoad() {
super.viewDidLoad()
style()

let searchInput = searchCityName.rx.controlEvent(.editingDidEndOnExit).asObservable()
.map { self.searchCityName.text }
.filter { ($0 ?? "").count > 0 }

let textSearch = searchInput.flatMap { text in
return ApiController.shared.currentWeather(city: text ?? "Error")
.catchErrorJustReturn(ApiController.Weather.dummy)
}

let mapInput = mapView.rx.regionDidChangeAnimated
.skip(1)
.map { _ in self.mapView.centerCoordinate }

let mapSearch = mapInput.flatMap { coordinate in
return ApiController.shared.currentWeather(lat: coordinate.latitude, lon: coordinate.longitude)
.catchErrorJustReturn(ApiController.Weather.dummy)
}

let currentLocation = locationManager.rx.didUpdateLocations
.map { locations in
return locations[0]
}
.filter { location in
return location.horizontalAccuracy < kCLLocationAccuracyHundredMeters
}

let geoInput = geoLocationButton.rx.tap.asObservable()
.do(onNext: {
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
})

let geoLocation = geoInput.flatMap {
return currentLocation.take(1)
}

let geoSearch = geoLocation.flatMap { location in
return ApiController.shared.currentWeather(lat: location.coordinate.latitude, lon: location.coordinate.longitude)
.catchErrorJustReturn(ApiController.Weather.dummy)
}

let search = Observable.from([geoSearch, textSearch, mapSearch])
.merge()
.asDriver(onErrorJustReturn: ApiController.Weather.dummy)

let running = Observable.from([searchInput.map { _ in true },
geoInput.map { _ in true },
mapInput.map { _ in true},
search.map { _ in false }.asObservable()])
.merge()
.startWith(true)
.asDriver(onErrorJustReturn: false)

running
.skip(1)
.drive(activityIndicator.rx.isAnimating)
.disposed(by: bag)

running
.drive(tempLabel.rx.isHidden)
.disposed(by: bag)

running
.drive(iconLabel.rx.isHidden)
.disposed(by: bag)

running
.drive(humidityLabel.rx.isHidden)
.disposed(by: bag)

running
.drive(cityNameLabel.rx.isHidden)
.disposed(by: bag)

search.map { "\($0.temperature)° C" }
.drive(tempLabel.rx.text)
.disposed(by: bag)

search.map { $0.icon }
.drive(iconLabel.rx.text)
.disposed(by: bag)

search.map { "\($0.humidity)%" }
.drive(humidityLabel.rx.text)
.disposed(by: bag)

search.map { $0.cityName }
.drive(cityNameLabel.rx.text)
.disposed(by: bag)

locationManager.rx.didUpdateLocations
.subscribe(onNext: { locations in
print(locations)
})
.disposed(by: bag)

mapButton.rx.tap
.subscribe(onNext: {
self.mapView.isHidden = !self.mapView.isHidden
})
.disposed(by: bag)

mapView.rx.setDelegate(self)
.disposed(by: bag)

search.map { [$0.overlay()] }
.drive(mapView.rx.overlays)
.disposed(by: bag)
}