Im new to RCA. I found this tut online ReactiveCocoaSamurai
Its pretty simple...In the original project they separate the model from the view controller, as they should :) But when I try to do it, I can't bind (or connect) the view controller property to the model property.
Everything is set up correctly because if I put all the code in the view controller class, it WORKS! The reason why it works is because I don't need the line of code in the view controller that binds the model to the view controller properties.
But when I try to split it up as the tutorial has it, I get an error at that line.
This works when in the view controller:
The Signal method
-(RACSignal *)forbiddenNameSignal {
return [RACObserve(self, username) filter:^BOOL(NSString *newName) {
return [self.forbiddenNames containsObject:newName]; //This yields a YES BOOLEAN?
}];
}
This goes in the viewDidLoad of the viewController:
[[self.usernameField.rac_textSignal distinctUntilChanged] subscribeNext:^(NSString *x) {self.username = x;}];
[self.mymodel.forbiddenNameSignal subscribeNext:^(NSString *name) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Forbidden Name!"
message:[NSString stringWithFormat:@"The name %@ has been forbidden!",name]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
self.mymodel.username = @"";
}];
Of course the UITextField is in storyboards and hooked up and the forbidden array is defined in viewDidLoad as well.
This doesn't work:
LoginModel.m
-(id)init {
self = [super init];
if(!self) return nil;
//forbidden names array
self.forbiddenNames = @[ @"Peter",@"Piper",@"Picker"];
return self;
}
-(RACSignal *)forbiddenNameSignal {
return [RACObserve(self, username) filter:^BOOL(NSString *newName) {
return [self.forbiddenNames containsObject:newName]; //This yields a YES BOOLEAN?
}];
}
ViewController.m viewDidLoad:
self.mymodel = [LoginModel new];
RAC(self.usernameField.text) = [RACAbleWithStart(self.mymodel.username) distinctUntilChanged];
[[self.usernameField.rac_textSignal distinctUntilChanged] subscribeNext:^(NSString *x) {
self.username = x;
}];
[self.mymodel.forbiddenNameSignal subscribeNext:^(NSString *name) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Forbidden Name!"
message:[NSString stringWithFormat:@"The name %@ has been forbidden!",name]
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alert show];
self.mymodel.username = @"";
}];
I get an error at the RAC line saying expected identifier & warning of "signalWithStarting..." deprecated for "valuesForKeyPath".
What I have tried:
RAC(self, usernameField) = [RACObserve(self, mymodel.username) distinctUntilChanged];
and ...
RAC(self, usernameField) = RAC(self, mymodel.username);
Any ideas how to fix this?
Or about why indenting code is not working either :(
I am not sure I fully understand your problem, but there are a few things that don't look right in your code.
Firstly, treat tutorials regarding ReactiveCocoa on the web with caution! When ReactiveCocoa 2.0 came out a few months ago there were a lot of breaking API changes. The RACAbleWithStart macro you have been trying to use is deprecated.
Also, this code looks wrong to me:
[[self.usernameField.rac_textSignal distinctUntilChanged]
subscribeNext:^(NSString *x) {self.username = x;}];
This is taking the rac_textSignal
and assigning the next event value to the username
property. Firstly, I am pretty sure distinctUntilChanged
is not required here, the signal will only emit events when the text field changes. Secondly, to assign the a signal to a property you can simply use the RAC macro:
RAC(self, username) = self.usernameField.rac_textSignal;
I see that the root of your problem is that you are trying to bind a property on your LoginModel
to a property in your view controller. ReactiveCocoa has the RACChannelTo
macro which is specifically for this purpose, however I have never used it, and have an idea that will side-step it!
In my opinion your LoginModel should become a view model, and it should expose a username
in order that it models the view (because that is what view models do!). You can then bind this within your view controller like so:
RAC(self.loginViewModel, username) = self.usernameField.rac_textSignal;
You then have to turn the username
property change into a signal within your view model, so that you can use your filter ... so within your LoginViewModel initialization code ...
[[RACObserve(self, username)
filter:^BOOL(NSString *username) {
return [self.forbiddenNames containsObject:username];
}
subscribeNext:^BOOL(NSString *forbiddenName) {
// do your thang!
}];
I hope that helps!