RxSwift Ⅲ:iOS 应用程序与 RxCocoa

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

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

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

第十二章:开始学习 RxCocoa

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 中级

添加搜索框支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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)
}