Search code examples
objective-cswiftswift3reactive-cocoareactive-cocoa-5

RACObserve(object, keyPath) in ReactiveCocoa 5.0


I want to monitor the UIButton.enabled property to change the button.titleColor

I have done in OC like this:

#import "ViewController.h"
#import <ReactiveCocoa/ReactiveCocoa.h>

@interface ViewController () <UITextViewDelegate>

@property (weak, nonatomic) IBOutlet UIButton *btn;


@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initUI];
}

- (void)initUI {

   __weak typeof(self) weakSelf = self;
   [[RACObserve(self.btn, enabled) map:^id(id value) {
    return [value boolValue] ? [UIColor greenColor] : [UIColor redColor];
   }] subscribeNext:^(id x) {
      [weakSelf.btn setTitleColor:x forState:UIControlStateNormal];
   }];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
     self.btn.enabled = !self.btn.enabled;
}

@end

Now, i try to achieve the same goal in swift use the lastest Version of ReactiveCocoa, how to do?


Solution

  • ReactiveCocoa 5.0 will add UIKit Extensions.

    With button.reactive.values(forKeyPath:)

    This works for me in a simple Playground:

    button.reactive.values(forKeyPath: "enabled")
        .map { $0 as? Bool }
        .skipNil()
        .map { enabled in enabled ? UIColor.blue : UIColor.red }
        .startWithValues { [weak button = button] color in
            button?.setTitleColor(color, for: .normal)
        }
    

    However, this is not encouraged because

    1. UIKit does not comply to KVO and if its working, its just coincidental.
    2. Arguably, your "truth" should not be in the UI / the button, but you should have a model somewhere that determines if the button is enabled or not.

    With a model

    In this example a simple MutableProperty is used as model, this could be in a ViewModel or wherever

    let buttonEnabled = MutableProperty<Bool>(false)
    
    button.reactive.isEnabled <~ buttonEnabled
    buttonEnabled.producer
        .map { enabled in enabled ? UIColor.blue : UIColor.red }
        .startWithValues { [weak button = button] color in
            button?.setTitleColor(color, for: .normal)
        }
    

    Unfortunaetly you can not bind directly to the buttons title color as you can with isEnabled