I'm trying to refactor my iOS app into ReactiveCocoa and ReactiveViewModel and I'm struggling with trying to work out a couple of best practices.
I'll boil this down to a simple use case - I was to push a view controller which loads some data and shoves it into a table view. If the endpoint call fails for whatever reason, I want to display a view on screen with a retry button.
I currently have this working but it looks a bit filthy. I feel like there must be a better way - am I even doing this correctly?
In my ViewModel's init
method, I'm creating my command, which is called as soon as the ViewModel becomes active.
// create the command to load the data
@weakify(self);
self.loadStationsCommand = [[RACCommand alloc] initWithSignalBlock:^(RACSignal *(id input) {
@strongify(self);
return [RACSignal createSignal:^(RACDisposable *(id<RACSubscriber subscriber) {
// load data from my API endpoint
...
BOOL succeeded = ...;
if (succeeded) {
[subscriber sendNext:nil];
[subscriber sendCompleted:nil];
} else {
// failed
[subscriber sendError:nil];
}
return nil;
}
}];
// start the command when the ViewModel's ready
[self.didBecomeActiveSignal subscribeNext:^(id x) {
@strongify(self);
[self.loadStationsCommand execute:nil];
}];
In my UIViewController, I'm subscribing to the command via -
[self.viewModel.loadStationsCommand.executionSignals subscribeNext:^(RACSignal *loadStationsSignal) {
[loadStationsSignal subscribeNext:^(id x) {
// great, we got the data, reload the table view.
@strongify(self);
[self.tableView reloadData];
} error:^(NSError *error) {
// THIS NEVER GETS CALLED?!
}];
}];
[self.viewModel.loadStationsCommand.errors subscribeNext:^(id x) {
// I actually get my error here.
// Show view/popup to retry the endpoint.
// I can do this via [self.viewModel.loadStationsCommand execute:nil]; which seems a bit dirty too.
}];
I must have some misunderstanding as to how RACCommand
works, or at the very least I feel I'm not doing this as cleanly as I can.
Why doesn't the error block on my loadStationsSignal
get called? Why do I need to subscribe to executionCommand.errors
instead?
Is there a better way?
It is a correct way to handle errors with
RACCommand
. As you can read in the docs, errors of the inner signal are not sent when usingexecutionSignals
:You can also use RAC additions to
UIButton
and bindself.viewModel.loadStationsCommand
torac_command
of theretry
button.There is a good article which explains RACCommand and shows some interesting patterns to be used with it.