Обработка следующего, завершенного и ошибки в ReactiveCocoa

Я все еще довольно новичок в мире ReactiveCocoa, и я просто хотел прояснить этот общий сценарий. Я заметил, что другие люди борются с этим вопросом на GitHub и SO, но мне все еще не хватает правильного ответа.

Следующий пример работает, но я видел, что Джастин Саммерс говорит, что подписки внутри подписок или подписки в целом могут быть запахом кода. Поэтому я хочу попытаться избежать вредных привычек при изучении этой новой парадигмы.

Итак, пример (с использованием MVVM) довольно прост:

  1. ViewController содержит кнопку входа в систему, которая связана с командой входа в модель представления.
  2. ViewModel определяет действие команды и имитирует некоторый сетевой запрос для этого примера.
  3. ViewController подписывается на executingSignals команды и может различать три типа возврата: следующий, ошибка и полный.

И код.

1 (Вьюконтроллер):

RAC(self.loginButton, rac_command) = RACObserve(self, viewModel.loginCommand);

2 (ВьюМодель):

self.loginCommand = [[RACCommand alloc] initWithEnabled:canLoginSignal 
                        signalBlock:^RACSignal *(id input) {
                            return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                        BOOL success = [username isEqualToString:@"user"] && [password isEqualToString:@"password"];
                        // Doesn't really make any sense to use sendNext here, but lets include it to test whether we can handle it in our viewmodel or viewcontroller
                        [subscriber sendNext:@"test"];
                            if (success) 
                            {
                                [subscriber sendCompleted];
                            } else {
                                [subscriber sendError:nil];
                            }

                        // Cannot cancel request
                        return nil;
                        }] materialize];
                    }];

3 (Вьюконтроллер):

[self.viewModel.loginCommand.executionSignals subscribeNext:^(RACSignal *execution) {
    [[execution dematerialize] subscribeNext:^(id value) {
        NSLog(@"Value: %@", value);
    } error:^(NSError *error) {
        NSLog(@"Error: %@", error);
    } completed:^{
        NSLog(@"Completed");
    }];
}];

Как бы вы сделали это в духе ReactiveCococa?


person Steffen D. Sommer    schedule 15.10.2014    source источник


Ответы (1)


При том, как работает RACCommand, значения поступают из сигнала executionSignals, ошибки из сигнала errors и завершения, ну, вот где можно использовать -materialize и -dematerialize, как в вашем примере.

В приведенном примере вход в систему, возможно, не требует завершения для его моделирования. Вместо этого сигнал входа в систему может быть определен как двоичный по поведению: он либо отправляет @YES (например), либо отправляет ошибку. В этих условиях код будет таким:

[[self.viewModel.loginCommand.executionSignals concat] subscribeNext:^(id _) {
    // Handle successful login
}];

[self.viewModel.loginCommand.errors subscribeNext:^(NSError *error) {
    // Handle failed login
}];

Это, очевидно, немного отличается от типичного шаблона subscribeNext:error:completed:, характерного для RAC. Это только из-за API RACCommand.

Обратите внимание, что оператор -concat был применен к executionSignals, чтобы отображать внутренние значения и избегать внутренних подписок. Вы также можете видеть, что -flatten или -switchToLatest используются в других примерах RACCommand, но всякий раз, когда команда имеет свойство allowsConcurrentExecution, установленное на NO (по умолчанию), выполнение происходит последовательно, что делает -concat оператором, который естественным образом соответствует и выражает эту последовательную семантику. Применение -flatten или -switchToLatest на самом деле будет работать, поскольку они вырождаются в -concat при применении к последовательным сигналам сигналов, но они выражают читателю семантику, которая не применяется.

person Dave Lee    schedule 24.10.2014
comment
Спасибо за ответ. Я согласен с вашим подходом, и это очень похоже на то, что у меня получилось. Ваше объяснение того, почему использовать concat в отличие от flatten, было очень полезным! - person Steffen D. Sommer; 01.11.2014