ReactiveCocoa: правильное использование сигналов для проверки доступности объекта
Я пытаюсь написать реактивное решение для следующего сценария.
При нажатии на кнопку, если в базе данных доступны некоторые объекты, пользователь должен быть перенаправлен в новый контроллер представления, в противном случае следует попытаться загрузить эти объекты и снова выполнить проверку.
Вот что у меня так далеко:
// VIEW CONTROLLER
self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return self.viewModel.rac_checkEntitiesAvailability;
}];
[self.button.rac_command.executionSignals.flatten subscribeNext:^(id x) {
if([x boolValue] == YES) {
// Entities available, can perform segue
} else {
// Error
}];
// VIEW-MODEL
- (RACSignal*)rac_checkEntitiesAvailability {
return [[RACSignal return:@([Entity MR_countOfEntities] > 0)]
flattenMap:^RACStream *(id entitiesAvailable) {
if([entitiesAvailable boolValue]) {
return [RACSignal return:@YES];
} else {
return [[[self rac_downloadEntities] flattenMap:^RACStream *(id value) {
// This takes into account network problems too
return [RACSignal return:@([Entity MR_countOfEntities] > 0)];
}];
}
}];
}
Кажется, это работает, но, поскольку я новичок в мире ReactiveCocoa, я не уверен, что он действительно правильный или может быть написан менее излишним способом.
Большое спасибо, ДАН
2 ответа
Я бы честно посоветовал не использовать прямую привязку событий управления, когда задействованы UIControls. Если вы используете RACCommand (объявленный и созданный в вашей viewModel), вы можете легко привязать свой пользовательский интерфейс к состоянию загрузки (выполнение сигнала), показывать предупреждающие сообщения при сбоях и представлять новый пользовательский интерфейс при успехе (при необходимости). Ваша исправленная версия кода кажется мне хорошей, но я бы, вероятно, упростил ваш внутренний сигнал: вам не нужно оборачивать логическую "немедленную" переменную вокруг возвращаемого сигнала (= вам не нужен сигнал, чтобы знать, сколько сущности, которые есть в вашей модели CoreData, это синхронная операция). Примерно так (проверьте синтаксис, это может быть неправильно со всеми этими квадратными скобками)
[RACSignal defer:^RACSignal *{
if([Entity MR_countOfEntities] > 0) {
return [RACSignal return:@YES];
} else {
return [[self rac_downloadEntities] flattenMap:^RACStream *(id value) {
// This takes into account network problems too
return [RACSignal return:@([Entity MR_countOfEntities] > 0)];
}];
}];
На самом деле, вам не нужно создавать сигнал для событий кнопки. MVVM используется для наблюдения за изменениями данных и автоматического изменения пользовательского интерфейса. Вы можете написать такие коды:
button.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
if (xxx) {
// Entities available, can perform segue
} else {
// Error
}
return [RACSignal empty];
}];
Или вы можете обрабатывать события напрямую:
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
if (xxx) {
// Entities available, can perform segue
} else {
// Error
}
}];
Не пытайтесь решить все проблемы через RAC.
===========
Для вашего сценария вы должны использовать блок. Добавьте функцию загрузки следующим образом:
@interface Entity ()
+ (int)MR_countOfEntities;
+ (void)MR_downloadEntites:(void(^)(void))finishBlock;
@end
@implementation Entity
static int _MR_countOfEntities = 0;
+ (int)MR_countOfEntities
{
return _MR_countOfEntities;
}
+ (void)MR_downloadEntites:(void (^)(void))finishBlock
{
// Download entites. This is an example.
[[AFHTTPRequestOperationManager manager] GET:@"http://test.com/entites" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
_MR_countOfEntities = 1;
finishBlock();
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
_MR_countOfEntities = 0;
finishBlock();
}];
}
@end
Таким образом, вы можете изменить коды кнопки следующим образом:
[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
if ([Entity MR_countOfEntities] > 1) {
// Entities available, can perform segue
} else {
[Entity MR_downloadEntites:^{
if ([Entity MR_countOfEntities] > 1) {
// Entities available, can perform segue
} else {
// Error
}
}];
}
}];