我正在使用ReactiveCocoa并尝试应用MVVM。我有一个相当典型的UITableView场景,带有用于重新加载数据的刷新控件。
我省略了UITableDataSource / Delegate方法,因为这些方法很简单。下面的代码说明了我如何设计ViewModel和ViewController以适应它们。
ViewModel.h
@property (strong, nonatomic, readonly) RACCommand *getItemsCommand;
@property (strong, nonatomic, readonly) NSArray *items;
ViewModel.m
- (instancetype)init {
self = [super init];
if (!self) return nil;
@weakify(self);
self.getItemsCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [[ItemsDataSource getItems]
doNext:^(NSArray *items) {
@strongify(self);
// I actually do a little extra work here such as sorting
// the items appropriately for the view.
self.items = items;
}];
}];
return self;
}
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView addSubview:self.refreshControl];
RACSignal *refreshSignals = [RACSignal merge:@[
[self.refreshControl rac_signalForControlEvents:UIControlEventValueChanged],
[RACSignal return:nil]
]];
[refreshSignals
subscribeNext:^(id x) {
[self.viewModel.getItemsCommand execute:nil];
}];
[RACObserve(self.viewModel, items)
subscribeNext:^(NSArray *items) {
[self.tableView reloadData];
} completed:^{
[self.refreshControl endRefreshing];
}];
}
问题/问题
completed
的{{1}}区块永远不会被执行,而在我的生命中,我无法弄清楚原因。endRefreshing
而不是- (RACSignal *)getItems
RACCommand会更好吗?getItems
是否正确,以便在不引起额外订阅的情况下应用副作用(即对items数组进行排序)?答案 0 :(得分:3)
我建议getItemsCommand
使用-map:
来排序和处理items数组。将任何其他副作用保留在单独的-doNext:
中。一旦您的命令遵循此模式(在RAC中更具组合性),您就可以使用RAC()
宏将命令的完成产品(已排序的数组)分配给items
属性。
RAC(self, items) = [self.getItemsCommand.executionSignals concat];
RAC具有对UIRefreshControl
的内置命令支持,它将启动/停止刷新控制以及命令的启动/停止。您应该会发现可以将UIRefreshControl
代码缩减为:
self.refreshControl.rac_command = self.getItemsCommand;
对于表格重新加载,您可以执行以下操作:
[RACObserve(self, items) subscribeNext:^(id _) {
@strongify(self);
[self.tableView reloadData];
}];
希望有所帮助。
答案 1 :(得分:2)
1)好吧,让我们来看看信号:
RACObserve(self.viewModel, items)
什么时候完成?仅当self.viewModel
或self
被取消分配时,就像其他任何RACObserve
一样。只要这些对象存在,只要您设置next
,它就会一直self.items
。
在endRefreshing
完成执行后,您似乎希望它getItemsCommand
,并且您有这种隐含的期望,因为您知道命令集self.viewModel.items
,所以完成将以某种方式传播 - 但事实并非如此。要查看命令何时完成,您必须订阅命令的返回信号。
2)RACCommand
的优点是自动启用/禁用行为,您在这里没有真正利用它。更典型的事情是self.refreshControl.rac_command = self.viewModel.getItemsCommand;
。那将为你处理endRefreshing
的东西,为你节省第1部分的麻烦。
3)......有点儿。 do*
系列方法为每次订阅信号注入了副作用。因此,如果您订阅了两次信号,并且它发送了下一个信号,则它将调用任何doNext
块两次。显式订阅更清晰,因为您希望每次执行此操作一次,无论订阅多少次。
@weakify(self);
self.getItemsCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
RACSignal *itemsSignal = [ItemsDataSource getItems];
[itemsSignal subscribeNext:^(NSArray *items) {
@strongify(self);
// Do stuff...
self.items = items;
}];
return itemsSignal;
}];