How to observe Bool properties with RxSwift?

13,433

Solution 1

Don't use Bool and rx_observe. Instead, use a Subject such as Variable<Bool>.

let isRecording = Variable(false)
let isPlaying = Variable(false)

let observable = Observable.combineLatest(isRecording.asObservable(), isPlaying.asObservable()) { (val1, val2) -> Void in
    //...

However, to answer your question as to why it doesn't work with rx_observe, it's because rx_observe relies on the property being KVO compliant. If you used the dynamic keyword in front of your property definitions, it would have worked: dynamic var isRecording = false. However, you should really be using a Subject such as Variable. Check out the playground page dealing with Subjects in the RxSwift repo. It gives an example of each type.

Solution 2

With latest RxSwift 5.0+ you can use BehaviorRelay

let isLoading = BehaviorRelay<Bool>(value: false)

And you can observe it's value as below:

isLoading.asObservable().subscribe { status in
                print(status)
            }.disposed(by: disposeBag)
Share:
13,433
Marina
Author by

Marina

IOS developer with 7 years experience. Currently working on VOIP IOS application.

Updated on June 16, 2022

Comments

  • Marina
    Marina almost 2 years

    I have two Bool properties in my VC.swift:

    var isRecording = false
    var isPlaying = false
    

    In the viewDidLoad() method I have such code:

    let observable = Observable.combineLatest(self.rx_observe(Bool.self, "isRecording"), self.rx_observe(Bool.self, "isPlaying")) { (val1, val2) -> Void in
            if(val1 == false && val2 == false){
                self.recordButton.enabled = true
                self.playButton.enabled = true
                self.recordButton.setImage(UIImage(named: "record"), forState: UIControlState.Normal)
                self.playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
            } else if(val1 == true && val2 == false){
                self.recordButton.enabled = true
                self.recordButton.setImage(UIImage(named: "stop"), forState: UIControlState.Normal)
                self.playButton.enabled = false
            } else if(val1 == false && val2 == true){
                self.recordButton.enabled = false
                self.playButton.enabled = true
                self.playButton.setImage(UIImage(named: "stop"), forState: UIControlState.Normal)
                self.recordButton.setImage(UIImage(named: "record"), forState: UIControlState.Normal)
            }
        }.observeOn(MainScheduler.instance)
        addSubscription(observable.subscribe())
    

    The function addSubscription(:_) adds subscription to DisposableBag. The problem is that the code in the closure works only once. It does not work when properties "isRecording" and "isPlaying" change. What should I do if I want this code to be performed after I have these bool properties changed?

  • Marina
    Marina almost 8 years
    Thank u very much. It works. But I used this: let observable = Observable.combineLatest(isRecording.asObservable(), isPlaying.asObservable()) { (val1, val2) -> Void in //...
  • solidcell
    solidcell almost 8 years
    Ah thanks for the catch. I've updated the answer. I also added an explanation as to why it wasn't working for you before.