简体   繁体   中英

iOS MVVM with Reactive Cocoa, UITextField return signal and condition : how to improve my code

I am trying to find a way to improve my code, it is not enough "Reactive" for me, but it works well.
I have a UITextfield and I want to send a signal (Log in my user) when I click on the the return button of the textfield, but only if a condition is met (I have a signal for that : registerIsAllowedSignal).

Actually I have a subscribe next on this signal, and I update a BOOL registerIsAllowed in it, and I have a manual check in my UITextfield return button subscribeNext to see if the registerIsAllowed == true, and in this case, I send a call my register method returning a signal : [self.loginViewModel register] .

I would like to improve my design by removing the "if" on the boolean, and directly using the registerIsAllowedSignal , but don't really know how to do it, my flow is :
- I need my registerIsAllowedSignal to have had return true the last he fired
- I need my UITextField return signal to have been the last to return true

So if anybody has leads on how I could improve that, it would be great, but also, if you think that my code is "Reactive enough" and I'm doing to do overkill stuff, please tell me :).

Thanks

Here is my actual code sample doing that :

self.logInAllowedSignal = [[RACSignal combineLatest:@[self.usernameIsValid, self.emailIsValid]] and];
[self.logInAllowedSignal subscribeNext:^(NSNumber *value) {
    self.logInAllowed = value.boolValue;
}];
self.registerAllowedSignal = [[RACSignal combineLatest:@[self.logInAllowedSignal, self.passwordIsValid]] and];
[self.registerAllowedSignal subscribeNext:^(NSNumber *value) {
    self.registerAllowed = value.boolValue;
}];

and later :

[[[tmpView.passwordTextField rac_keyboardReturnSignal] doNext:^(id x){
    [tmpView.usernameTextField resignFirstResponder];
}]
subscribeNext:^(id x) {
    @strongify(self);
    if (self.registerAllowed)
    {
        [[self.loginViewModel register] subscribeNext:^(id x) {
            NSLog(@"Register response : %@", x);
        }];
    }
}];

Depending on what should happen or what [self.loginViewModel register] returns, I would probably do something like this:

@weakify(self);
self.logInAllowedSignal = [[RACSignal
    combineLatest:@[self.usernameIsValid, self.emailIsValid]]
    and];

self.registerAllowedSignal = [[RACSignal
    combineLatest:@[self.logInAllowedSignal, self.passwordIsValid]]
    and];

RAC(self, someUserObject) = [[[[[tmpView.passwordTextField rac_keyboardReturnSignal]
    // resignFirstResponder uses side-effects so we can't avoid the doNext.
    doNext:^(id _) {
        [tmpView.usernameTextField resignFirstResponder];
    }]
    flattenMap:^RACStream *(id _) {
        @strongify(self);
        return self.registerAllowedSignal;
    }]
    filter:^BOOL(NSNumber *allowedToRegister) {
        return [allowedToRegister boolValue];
    }]
    flattenMap:^RACStream *(id _) {
        @strongify(self);
        return [self.loginViewModel register];
    }];

Few things to notice:

  • Try to avoid side-effects (eg by using subcribeNext , doNext etc.)
  • Avoid using your own state booleans - use the signals directly.
  • Consider doing formatting as proposed by the authors
  • Try to avoid creating retain cycles (in my example I use the macros @weakify and @strongify ).

I assume that the [self.loginViewModel register] is returning a signal which we need to get started. Instead of doing a subscribeNext , try to combine it with what you want to do with the result of the signal. In my example I'm binding some user object to the result, but it could also be that the result should trigger a view transition? The key thing to remember is to avoid side-effects when possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM