Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the preferred way to create a signal of signals to combine to signals and complete when either completes or errors?

I have success notification and a failure notification that come through NSNotificationCenter. I wrote some tests to figure out how to combine the signals from those two notifications into one signal that provides an error when the failure notification hits and a next followed by a complete when the success notification hits.

Currently the complete blocks won't get hit, next and error get hit.

Also, secondary bonus question: why doesn't @[errorNotification, completeNotification].rac_sequence.signal do the same thing as the signal of signal creation below?

Code:

-(void)test_flatten_signal_of_signals_and_convert_notification_to_error{
    RACSignal *errorNotification = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"TEST_FAILURE" object:nil] take:1];


    errorNotification = [errorNotification flattenMap:^(NSNotification *notification){
        return [RACSignal error:[NSError errorWithDomain:@"RAC_TEST" code:1 userInfo:nil]];
    }];

    RACSubject *completeNotification = [RACSubject subject];

    RACSignal *signalOfSignals = [[RACSignal
                                   createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
                                       [subscriber sendNext:errorNotification];
                                       [subscriber sendNext:completeNotification];
                                       [subscriber sendCompleted];
                                       return nil;
                                   }]
                                  flatten];


    __block BOOL hitCompleted = NO;

    [signalOfSignals
     subscribeNext:^(id val){
         STFail(nil);
     }
     error:^(NSError *err){
         hitCompleted = YES;
     }
     completed:^{
         STFail(nil);
     }];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"TEST" object:self];

    STAssertTrue(hitCompleted, nil);
}

-(void)test_flatten_signal_of_signals_and_hits_next_complete_on_notification{
    RACSubject *errorNotification = [RACSubject subject];

    RACSignal *completeNotification = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"TEST_SUCESS" object:nil] take:1];

    RACSignal *signalOfSignals = [[RACSignal
                                   createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
                                       [subscriber sendNext:errorNotification];
                                       [subscriber sendNext:completeNotification];
                                       [subscriber sendCompleted];
                                       return nil;
                                   }]
                                  flatten];


    __block BOOL hitCompleted = NO;
    __block BOOL hitNext = NO;
    [signalOfSignals
     subscribeNext:^(id val){
         hitNext = YES;
     }
     error:^(NSError *err){
         STFail(nil);
     }
     completed:^{
         hitCompleted = YES;
     }];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"TEST_SUCCESS" object:self];

    STAssertTrue(hitCompleted, nil);
    STAssertTrue(hitNext, nil);
}
like image 794
Jon Avatar asked Aug 04 '13 05:08

Jon


1 Answers

You can do this with built-in operators:

RACSignal *successNotification = [[NSNotificationCenter.defaultCenter
    rac_addObserverForName:SuccessNotification object:nil]
    take:1];

RACSignal *errorNotification = [[NSNotificationCenter.defaultCenter
    rac_addObserverForName:FailureNotification object:nil]
    flattenMap:^(NSNotification *notification) {
        // Convert to a meaningful error somehow.
        NSError *error = …;

        return [RACSignal error:error];
    }];

RACSignal *signal = [RACSignal merge:@[ successNotification, errorNotification ]];

This takes care of disposal for you, and more obviously indicates how each of the notifications are mapped to a value or an error.

why doesn't @[errorNotification, completeNotification].rac_sequence.signal do the same thing as the signal of signal creation below?

The signal created will send its values asynchronously, unlike the signal you created in your example.

like image 189
Justin Spahr-Summers Avatar answered Nov 02 '22 22:11

Justin Spahr-Summers