RxSwiftの公式Exampleを覗いてみる(2)
前回に引き続きRxSwiftの公式Exampleを見ていきたいと思います。
公式ソースはGitHub: ReactiveX/RxSwiftからダウンロードできます。
GeolocationExample
位置情報を用いたときのRxの有効性が表現されているのでしょうか?
早速見ていきましょう。
まずは、CoreLocation
のコア部分をラップしているGeolocationService
です。
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
| import Foundation
import CoreLocation
import RxSwift
import RxCocoa
class GeolocationService {
static let instance = GeolocationService()
// (1)
private (set) var authorized: Driver<Bool>
private (set) var location: Driver<CLLocationCoordinate2D>
private let locationManager = CLLocationManager()
private init() {
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation
// (2)
authorized = Observable.deferred { [weak locationManager] in
let status = CLLocationManager.authorizationStatus()
guard let locationManager = locationManager else {
// (3)
return Observable.just(status)
}
return locationManager
.rx_didChangeAuthorizationStatus
.startWith(status) // (4)
}
.asDriver(onErrorJustReturn: CLAuthorizationStatus.NotDetermined) // (5)
.map {
switch $0 {
case .AuthorizedAlways:
return true
default:
return false
}
}
}
}
|
上記ソースでポイントとなる部分を見ていきます。
(1): Driver
末尾に参考URLとして上げさせて頂いた記事に書かれているのですが、
Driver
型で定義することで、エラー発生時の処理asDriver
オペレータで続けて書くことができます。
(2): deferred
ObserverがSubscribe(購読)されたタイミングで動的にObservableを生成します。
※ 因みに新規ObservableをSubscribeタイミングで毎回生成します。
(3): just
引数に取った特定の型を返却するObservableを生成します。
この場合はCLAuthorizationStatus
型のObservableです。
(4): startWith
想定したemit対象値の前に何らかの値をemitしたい場合に利用します。
この場合、rx_didChangeAuthoricationStatus
をemitする前にということでしょうか。
(これが恐らく、一番わかりやすい例です → Introduction to Rx: startWith)
(5): asDriver
(1)で説明したDriver
に関係するオペレータです。
エラーが発生した場合にonErrorJustReturn
で指定した値を返却して処理を続けます。
続いて、GeolocationService
を利用しているGeolocationViewController
です。
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
| import Foundation
import UIKit
import RxSwift
import RxCocoa
// (6)
private extension UILabel {
var rx_driveCoordinates: AnyObserver<CLLocationCoordinate2D> {
return UIBindingObserver(UIElement: self) { label, location in
label.text = "Lat: \(location.latitude)\nLon: \(location.longitude)"
}.asObserver()
}
}
// (7)
private extension UIView {
var rx_driveAuthorization: AnyObserver<Bool> {
return UIBindingObserver(UIElement: self) { view, authorized in
if authorized {
view.hidden = true
view.superview?.sendSubviewToBack(view)
}
else {
view.hidden = false
view.superview?.bringSubviewToFront(view)
}
}.asObserver()
}
}
class GeolocationViewController: ViewController {
@IBOutlet weak private var noGeolocationView: UIView!
@IBOutlet weak private var button: UIButton!
@IBOutlet weak private var button2: UIButton!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let geolocationService = GeolocationService.instance
geolocationService.authorized
.drive(noGeolocationView.rx_driveAuthorization) // (8)
.addDisposableTo(disposeBag)
geolocationService.location
.drive(label.rx_driveCoordinates) // (8)
.addDisposableTo(disposeBag)
button.rx_tap
.bindNext { [weak self] in
self?.openAppPreferences()
}
.addDisposableTo(disposeBag)
button2.rx_tap
.bindNext { [weak self] in
self?.openAppPreferences()
}
.addDisposableTo(disposeBag)
}
private func openAppPreferences() {
UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
}
}
|
(6): rx_driveCoordinates
見ての通りここでUILabel
のextension
をしています。
UILabel
のtext
内容を任意の値で返却するために作成しています。
(asObserver
をつけることでrx_driveCoordinates
というAnyObserver
型の変数定義を実現しています。)
(7): rx_driveAuthorization
同じくUIView
のextension
をしています。
authorized
の値でViewの表示/非表示を切り替えています。
(8): drive
ここで新たにSubscriptionを生成して、引数に取ったObserverに処理の実行を要請しています。
今回のExampleを見てみると下記のようなメリットが感じられます。
- Rxを利用することで非同期処理を直列的に書ける
- エラーハンドリングを直列的に書けることで後処理も直列的に統一して見れる(
jQuery
のajax
メソッドのalways
的なイメージ)
- 処理の拡張がRxで用意されたメソッドで比較的に容易に書ける
Rxで把握しておきたいAPI一覧
RxSwiftをインストールすると中にドキュメントが含まれています。
API.mdを読むだけでもかなり理解が進むと思われます。
特に言語がSwiftであるが故に他のRxフレームワークとはメソッド名が異なる場合があります。
(defer
でなくdeferred
、repeat
でなくrepeatElement
など)
一度は目を通しておくと良いかもしれません。
と言いつつ、筆者も全然見れていませんが…
まとめ
さて、今回は公式Exampleの1つを見るだけに留まりましたが如何でしたでしょうか?
筆者としてはRxの使い方の理解がだいぶ進んだ気がしております。
引き続きRxに関する勉強は続けていきたいと思います。
と言ったところで本日はここまで。
参考: