なんとかするから、なんとかなる

エンジニア関係のことを書きます

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