Processing next, complete and error in ReactiveCocoa

I'm still pretty new to the ReactiveCocoa world and I just wanted this general scenario to be clarified. I noticed that other people are struggling with this case on GitHub and SO, but I am still not getting a proper answer.

The following example works, but I saw Justin Summers say that subscriptions - within a subscription or subscriptions in general can be code smells. Therefore, I want to try to avoid bad habits while learning this new paradigm.

So the example (using MVVM) is pretty simple:

  • ViewController contains a login button that is connected to the login command in view mode
  • The ViewModel specifies the command action and simulates some network request for this example.
  • The ViewController subscribes to the executeSignals command and can differentiate between three return types: next, error, and complete.

And the code.

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

      

How would you do it in the more active ReactiveCococa mode?

+3


source to share


1 answer


As it works RACCommand

, values ​​come from signal executionSignals

, errors from signal errors

and end up with, well, that's where to use -materialize

and -dematerialize

like in your example.

In the above example, the login may not need to be completed to simulate it. Instead, a boolean signal can be defined as binary in behavior: it either sends @YES

(for example) or sends an error message. Under these conditions, the code will look like this:

[[self.viewModel.loginCommand.executionSignals concat] subscribeNext:^(id _) {
    // Handle successful login
}];

[self.viewModel.loginCommand.errors subscribeNext:^(NSError *error) {
    // Handle failed login
}];

      



This is obviously a bit at odds with the typical subscribeNext:error:completed:

RAC pattern . It has to do with the API RACCommand

.

Note that the operator has -concat

been applied to executionSignals

to align internal values ​​and avoid internal signatures. You can also see -flatten

or -switchToLatest

used in other examples RACCommand

, but whenever a command has a property allowsConcurrentExecution

set to NO

(which is the default), execution is serialized, making a -concat

statement that naturally matches and expresses that consistent semantics. Applying -flatten

or -switchToLatest

will indeed work as they degenerate to -concat

when applied to sequential cues, but they express semantics to the reader that doesn't apply.

+8


source







All Articles