2014-10-15 3 views
3

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

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

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

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

И код.

1 (ViewController):

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

2 (ViewModel):

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 (ViewController):

[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- вид-а-путь?

ответ

8

С путем 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, когда применяются к последовательным сигналам-сигналам, но они выражают семантику читателю, который не применяется.

+0

Спасибо за ответ. Я согласен с вашим предоставленным подходом, и это очень похоже на то, с чем я закончил.Ваше объяснение о том, почему использовать concat в отличие от сглаживания, было очень полезно! –