使用 Reactivecocoa 登录按钮只触发一次

Using Reactivecocoa login button only triggered once

这是我的基本场景:

我正在制作一个简单的 login 框架,所以我得到了两个 text field,一个用于用户名,另一个用于密码,当然还有一个登录名 button.

现在我像这样用 RACSignal 绑定它们:

RACSignal *validPasswordSignal = [passwordTextField.rac_textSignal map:^id(NSString *text)
{
    return @([self isPasswordValid:text]);
}];

RACSignal *validUsernameSignal = [usernameTextField.rac_textSignal map:^id(NSString *text)
{
    return @([self isUsernameValid:text]);
}];

然后像这样将两个信号(用户名和密码)合二为一:

RACSignal *submitActiveSignal = [RACSignal combineLatest:@[validpasswordSignal, validUsernameSignal] reduce:^id(NSNumber *validPW, NSNumber *validUN)
{
    return @(validPW.boolValue && validUN.boolValue);
}];

当然我需要一种方法来向服务器提交用户名和密码的信号,所以我有这样的方法:

-(RACSignal *)submitSignal
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
    {
        [[[RequestClass alloc] init] requestWithUsername:_usernameTextField.text.trim password:_passwordTextField.text.trim completionHandler:^(MyResult *results, NSURLResponse *response, NSError *error)
        {
            if (!error)
            {
                if (results.code == 0)
                {
                    [subscriber sendNext:@(YES)];
                    [subscriber sendCompleted];
                }
                else
                {
                    NSError *invalidError = [NSError errorWithDomain:MyErrorDomain code:MyErrorInvalidSigniture userInfo:@{@"NSLocalizedDescription": results.message}];
                    [subscriber sendError:invalidError];
                }
            }
            else
            {
                [subscriber sendError:error];
            }
        }];
        return nil;
    }];
}

然后我需要让我的登录按钮捕捉来自订阅者 sendNextsendError 的信号,所以我这样做了:

//subscribeNext
[[[[_submitButton rac_signalForControlEvents:UIControlEventTouchUpInside]
doNext:^(id x)
{
    _submitButton.enabled = NO;
}]
flattenMap:^RACStream *(id value)
{
    return [self submitSignal];
}]
subscribeNext:^(NSNumber *signal)
{
    _submitButton.enabled = YES;
    BOOL success = signal.boolValue;
    if (success)
    {
        [self successCallback];
    }
    else
    {
        //do sth
    }
}];

//subscribeError
[[[[_submitButton rac_signalForControlEvents:UIControlEventTouchUpInside]
doNext:^(id x)
{
    _submitButton.enabled = NO;
}]
flattenMap:^RACStream *(id value)
{
    return [self submitSignal];
}]
subscribeError:^(NSError *error)
{
    [self failureCallbackWithError:error];
}];

以上是我为登录程序编写的 reactivecocoa 代码。当通行证和用户名正确时,信号将转到 sendNext 的响应块:subscribeNext, 但问题是,当凭据不正确并且服务器返回错误时,sendErrorsubscribeError 都不会被触发。

好像错误信号丢失了之类的。

我不确定我是否使用了正确的 reactivecocoa 方法来处理成功信号和错误信号。

所以请帮忙,谢谢。

另外,如果我的问题不清楚,请告诉我。

您可以使用 RACCommand,它会为您处理很多事情:

@weakify(self)
RACCommand *submitCommand = 
    [[RACCommand alloc] initWithEnabled:submitActiveSignal signalBlock:^RACSignal *(id input) {
        @strongify(self)
        return [[self submitSignal]
            doCompleted:^{
                @strongify(self)
                [self successCallback];
            }];
    }];

_submitButton.rac_command = submitCommand;

通过提供 submitActiveSignal 作为 initWithEnabled: 参数,按钮将根据该信号发出的值自动 enabled/disabled。

对于错误处理,RACCommand 有一个特殊的 errors 信号发送命令中出现的每个错误。您可以订阅 errors 信号来处理从命令内部返回的信号导致的所有错误:

[[submitCommand.errors 
    takeUntil:self.rac_willDeallocSignal]
    subscribeNext:^(NSError *error) {
        @strongify(self)
        [self failureCallbackWithError:error];
    }];

并且不要忘记在信号内部 @weakify/@strongify 引用 self 以避免内存泄漏。