Отправить ошибку из блока doNext?
Я использую ReactiveCocoa и Overcoat/Mantle/AFNetworking для извлечения данных и аутентификации пользователя из RESTful API.
Это код в контроллере представления входа в систему, который управляет кнопкой входа в систему и текстовыми полями для учетных данных:
@weakify(self);
self.loginButton.rac_command =
[[RACCommand alloc] initWithEnabled:validCredentials
signalBlock:^RACSignal *(id input) {
@strongify(self);
return [[PFUserManager sharedManager] logInUser:self.usernameTextField.text
password:self.passwordTextField.text];
}];
// Handle errors for the login command
[self.loginButton.rac_command.errors subscribeNext:^(NSError *error) {
// Present the error message
[PFErrorAlertFactory showOVCError:error];
}];
// Take care of the signal from the request
[[self.loginButton.rac_command.executionSignals flatten] subscribeNext:^(NSNumber *success) {
@strongify(self);
[self clearTextFields];
[self.flowController controllerForMainScreen]; // Transition to "logged in state"
} error:^(NSError *error) {
@strongify(self);
[self clearTextFields];
}];
У меня есть этот метод на одноэлементном классе UserManager:
- (RACSignal *)logInUser:(NSString *)username password:(NSString *)password {
// Return a cold signal that sends next and complete when user is authenticated and error if authentication failed.
PFAPIClient *client = [[PFAPIClient alloc] initWithUsername:username password:password];
@weakify(self);
RACSignal *loginSignal = [[client rac_POST:kAuthenticationResourcePath parameters:nil] doNext:^(OVCResponse *response) {
@strongify(self);
self.currentUser = response.result;
NSError *error;
[SSKeychain setPassword:password forService:kKeychainServiceKey account:self.currentUser.username error:&error];
if (error) {
[PFErrorAlertFactory showLocalizedDescriptionOfError:error];
}
}];
return loginSignal;
}
Это все хорошо, используя этот сигнал в качестве сигнала RACCommand для кнопки. Я обрабатываю следующие, ошибки и завершенные события в контроллере представления входа в систему, и он работает нормально.
Как вы видите в коде UserManager, в блоке doNext я показываю ошибку, если метод Keychain возвращает ее. Я немного не уверен, относится ли эта обработка ошибок к этому классу.
Это работает, ошибка отображается как UIAlertView, но должен ли этот класс UserManager действительно отвечать за отображение ошибки?
Ошибки, поступающие из сигнала rac_POST, обрабатываются контроллером представления входа в систему, и я хотел бы также обработать ошибку из метода Keychain. Можно ли отправить ошибку абоненту сигнала rac_POST из блока doNext? Я пропускаю указатель на подписчика, хотя... Кроме того, если в методе цепочки для ключей происходит ошибка, сигнал все еще отправляет следующий и завершенный, и вход в систему успешен, насколько знает вызывающий контроллер представления. Это явно не так, как это должно работать.
Есть ли другой предпочтительный способ справиться с этой ситуацией? Я знаю, что побочные эффекты в блоках doNext не являются предпочтительными, но в этом случае я не вижу другого решения, так как я хочу, чтобы UserManager владел этим методом и мог устанавливать свой собственный currentUser. Должен ли я обернуть это в новый сигнал и явно вместо этого отправить next, complete и error?
С уважением, Йенс
1 ответ
Вы можете создать SSKeychain
категория:
@interface SSKeychain (RACExtension)
- (RACSignal*)rac_setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
@end
@implementation SSKeychain (RACExtension)
- (RACSignal*)rac_setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account
{
[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSError *error;
BOOL result = [SSKeychain setPassword:password forService:service account:account error:&error];
if (result) {
[subscriber sendNext:@(result)];
[subscriber sendCompleted];
} else {
[subscriber sendError:error];
}
}];
}
@end
А потом используй свой UserManager
:
- (RACSignal *)logInUser:(NSString *)username password:(NSString *)password
{
PFAPIClient *client = [[PFAPIClient alloc] initWithUsername:username password:password];
@weakify(self);
return [[[client rac_POST:kAuthenticationResourcePath parameters:nil]
flattenMap:^RACStream *(OVCResponse *response){
@strongify(self)
return [[SSKeychain rac_setPassword:password forService:kKeychainServiceKey account:self.currentUser.username]
mapReplace:response];
}]
doNext:^(OVCResponse *response){
@strongify(self)
self.currentUser = response.result;
}]
}