RxSwift ObserverとPublishObject, BehaviorSubject
はじめに
今さらながらRxSwiftを試して見ました。
Disposable
- Observerしている情報を保持しているオブジェクト
- Disposable.dispose()を呼び出すことで通知の解除が可能
- 複数のDiposableを一度に処理するためにDisposableBagがある
ObserverとPublishObject
- Observerは通知を認識することができるオブジェクト
- PublishObjectは通知する対象のオブジェクト
- PublishObjectをObservableでWrapすることで、別オブジェクトのObserverが変化を認識することができる
MyClass.Swift
- 通知元はPrivateでPublishSubjectとして宣言
- 値を通知するために、observableでwrapして別クラスから通知を受け取れるようにする。
- func doSomething は値を通知するだけのメソッド。
- onNextで値の通知を示し、onNextの引数で通知する値を設定する。
import Foundation import RxSwift class MyClass { private let publicSubject = PublishSubject<Int>() var event: Observable<Int> { return eventSubject } func doSomething() { eventSubject.onNext(1) } }
ViewController.swift
呼び出し元では、次のように宣言。
- disposableで値の通知を受けとる対象を定義
- 値が通知された場合の挙動を定義する。
- buttonPushedで値を通知させてみる
import UIKit import RxSwift class ViewController: UIViewController { let myClass = MyClass() var disposable: Disposable? override func viewDidLoad() { super.viewDidLoad() disposable = myClass.event.subscribe( onNext: { (value) in // 値が通知されるごとに呼ばれる print("OnNext") print(value) }, onError: { (error) in // エラーが発生した場合に呼ばれる print(error) }, onCompleted: { // 通知止められ今後通知されない時に呼ばれる print("onCompleted") }) } @IBAction func buttonPushed(_ sender: Any) { myClass.doSomething() } }
結果
OnNext 1
onNext(1)で値が通知されているのがわかります。
ObserverとBehaviorSubject
- BehaviorSubjectは値自体であり、変化を通知することができるオブジェクト
- Observerで通知を認識することができる
- BehaviorSubjectをWrapしたObservable自体を値として扱うことができる。
MyClass.swift
- BehaviorSubjectでBool型として値を定義。初期値を設定しておく
- ObservableでWrapして通知を受け取れるようにする
- Variableで省略することも可能
- func doSomethingで値を変更
import Foundation import RxSwift class MyClass { private let behaviorSubject = BehaviorSubject<Bool>(value: false) var buttonHidden: Observable<Bool> { return behaviorSubject } func doSomething() { let oldValue = try? behaviorSubject.value() if oldValue != nil { behaviorSubject.onNext(!oldValue!) } } }
ViewController.swift
- disposable2で値の変化を認識
- onErrorやonCompletedが不要な場合は省略可
- disposable2で通知が来た時の挙動を定義する。
import UIKit import RxSwift class ViewController: UIViewController { @IBOutlet weak var label: UILabel! let myClass = MyClass() var disposable2: Disposable? override func viewDidLoad() { super.viewDidLoad() disposable2 = myClass.buttonHidden.subscribe({ (value) in self.label.isHidden = value.element! }) // このような書き方もOK // disposable2 = myClass.buttonHidden.bindTo(button.rx.isHidden) } @IBAction func buttonPushed(_ sender: Any) { myClass.doSomething() } }
結果
- ボタンがタップされるたびにLabelが見えたり消えたりします
おわりに
基本的な動きとしては、Disposableで値を受け取って処理するようです。
(値).bindTo(UILabelオブジェクト)という記述方法が、従来と比べて直感的ではない気がします。
単純なBindであれば次のような記述でバインドしてはくれないだろうか。
label.rx.isHidden = myClass.buttonHidden
演算子拡張してそのうちやって見たいですね。
今後も定期的に勉強していきたいです。 次回はイベント処理について勉強したいです。
参考URL
https://github.com/ReactiveX/RxSwift https://qiita.com/k5n/items/17f845a75cce6b737d1e