ReactiveCocoa:正确使用信号来检查实体可用性

时间:2015-08-23 17:41:04

标签: ios objective-c reactive-programming reactive-cocoa

我正在尝试为以下场景编写反应式解决方案。

点击按钮,如果数据库中有某些实体,则必须将用户推送到新的视图控制器,否则应尝试下载这些实体并再次执行检查。

这是我到目前为止所得到的:

// 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世界的新手,我不确定它是否真的正确或者可以用不那么冗余的方式编写。

非常感谢, DAN

2 个答案:

答案 0 :(得分:1)

实际上,您不需要为按钮事件创建信号。 MVVM用于观察数据更改和自动更改UI。您可以编写如下代码:

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
            }
        }];
    }
}];

答案 1 :(得分:1)

我会诚实地建议在涉及UIControl时不要直接绑定控制事件。如果您使用RACCommand(在viewModel中声明和创建),您可以轻松地将UI绑定到下载状态(执行信号),在失败时显示警报消息,并在成功时显示新UI(如果需要)。 您修改后的代码版本对我来说似乎很好,但我可能会简化您的内部信号:您不需要在返回信号周围包含一个布尔“立即”变量(=您不需要信号就知道有多少您在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)];
    }];
  }];