ref: http://www.sprynthesis.com/2014/12/06/reactivecocoa-mvvm-introduction/
The MVVM relies on data-binding, a framework level feature that automatically connects object properties to UI controls. But iOS lacks a data-binding framework.
Controller:
Controller holds the viewModel with weak attribute.
RWTFlickrSearchViewController.h:
@interface RWTFlickrSearchViewController : UIViewController
- (instancetype)initWithViewModel:(RWTFlickrSearchViewModel *)viewModel;
@end
RWTFlickrSearchViewController.m:
@property (weak, nonatomic) RWTFlickrSearchViewModel *viewModel;
- (instancetype)initWithViewModel:(RWTFlickrSearchViewModel *)viewModel {
self = [super init];
if (self ) {
_viewModel = viewModel;
}
return self;
}
- (void)viewDidLoad {
// ...
[self bindViewModel];
}
- (void)bindViewModel {
self.title = self.viewModel.title;
// 每次textField的值发生变化, viewModel相对应的property都会相应的改变
RAC(self.viewModel, searchText) = self.searchTextField.rac_textSignal;
// button taps result in the given command executing
// the "enabled" state of the button reflects the enabled state of the command.
self.searchButton.rac_command = self.viewModel.executeSearch;
// whenever the command executes, the small network activity indicator in the status bar will show itself.
RAC([UIApplication sharedApplication], networkActivityIndicatorVisible) =
self.viewModel.executeSearch.executing;
//
RAC(self.loadingIndicator, hidden) =
[self.viewModel.executeSearch.executing not];
[self.viewModel.executeSearch.executionSignals
subscribeNext:^(id x) {
[self.searchTextField resignFirstResponder];
}];
}
RAC: binding self.viewModel.searchText with the content in the signal self.searchTextField.rac_textSignal;
RACCommand exposes an executing property, and that’s a signal that emits true and false events to indicate when the command starts and ends execution.
The executionSignals property emits the signals that generate each time the command executes.
ViewModel:
RWTFlickrSearchViewModel.h
@interface RWTFlickrSearchViewModel : NSObject
@property (strong, nonatomic) NSString *searchText;
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) RACCommand *executeSearch;
@end
RACCommand: a ReactiveCocoa concept that represents a UI action. It comprises a signal, which is the result of the UI action, and the current state, which indicates whether the action is currently being executed.
RWTFlickrSearchViewModel.m
- (instancetype)init {
self = [super init];
if (self) {
[self initialize];
}
return self;
}
- (void)initialize {
self.title = @"Flickr Search";
// 观测property
RACSignal *validSearchSignal =
[[RACObserve(self, searchText)
map:^id(NSString *text) {
return @(text.length > 3);
}]
distinctUntilChanged]; // distinctUntilChanges is used to ensure this signal only emits values when the state changes.
[validSearchSignal subscribeNext:^(id x) {
NSLog(@"search text is valid %@", x);
}];
// 创建search command: 每当validSearchSignal emits true, 就return信号
self.executeSearch =
[[RACCommand alloc] initWithEnabled:validSearchSignal
signalBlock:^RACSignal *(id input) {
return [self executeSearchSignal];
}];
}
- (RACSignal *)executeSearchSignal {
return [[[[RACSignal empty]
logAll]
delay:2.0]
logAll];
}