Search code examples
ios7reactive-cocoaracsignal

How to use RACMulticastConnection for app components notification


Short version: Is it possible to use RACMulticastConnection in the same way as NSNotificationCenter? I mean to keep the subscribed blocks valid even for another call [connection connect]?

Long version: Among different subscribers I share a reference to RACMulticastConnection. Those who initiate the request pass loginRequest!=nil and those subscribers who want just listen use loginRequest==nil:

RACMulticastConnection *connection = [self.appModel loginRequest:loginRequest];
[connection connect]; //I initiate the request


[connection.signal subscribeNext:^(RACTuple* responseTuple) {

} error:^(NSError *error) {

} completed:^{

}];

In other modul I just subscribe and listen:

RACMulticastConnection *connection = [self.appModel loginRequest:nil];
[connection.signal subscribeNext:^(RACTuple* responseTuple) {

} error:^(NSError *error) {

} completed:^{

}];

When I call the [connection connect]; everything works fine. The subscribers are notified. But if I want to repeat the request to the server again with [connection connect]; I just receive successful signal with the old responses.

Basic idea is I want to create RACMulticastConnection once and share it for potential subscribers. Those who listen pass nil arguments, those who initiate the request pass not nil argument and call [connection connect]; But it does not trigger the block defined in RACSignal createSignal:.

The RACSignal is created just once when RACMulticastConnection does not exist. self.loginRequestConnection is property of model. The property is shared in the application to subscribers:

- (RACMulticastConnection*) loginRequest:(LoginRequest*)request {

    self.loginRequest = request;

    if(! self.loginRequestConnection) { // the instance of RACMulticastConnection shared among the subscribers

        @weakify(self);
        RACSignal* networkRequest = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

            __block Response* blockResponse = nil;

            @strongify(self);

            [_serviceClient login:self.loginRequest success:^(TSNApiResponse *response, TSNError *error) {

                blockResponse = response;
                [subscriber sendNext:RACTuplePack(response, error)];
                [subscriber sendCompleted];

            } failure:^(TSNError *error) {

                [self cleanUpRequestOnError:error subscriber:subscriber blockResponse:blockResponse blockRequest:self.loginRequest];

                [subscriber sendError:error];
            }];

            return [RACDisposable disposableWithBlock:^{

                [_serviceClient cancelRequest:self.loginRequest];
            }];

        }];

        self.loginRequestConnection = [networkRequest multicast:[RACReplaySubject subject]];
    }

    return self.loginRequestConnection;
}

Is there any correct way how to make the connection trigger again the block in the RACSignal? Thank you.


Solution

  • I have used FBKVOController. The code done with FBKVOController is fraction of the implementation done with ReactiveCocoa:

    - (void) loginRequest:(TSNLoginRequest*)request {
    
        [_serviceClient login:request success:^(TSNApiResponse *response, TSNError *error) {
    
            self.loginResponseTuple = [TSNResponseTuple responseTuple:response error:error];
    
        } failure:^(TSNError *error) {
    
            self.loginResponseTuple = [TSNResponseTuple responseTuple:nil error:error];
    
       }];
    }
    

    Issuing the request:

    [self.appModel loginRequest:loginRequest];
    

    Observing the response:

    [_KVOController observe:self.appModel keyPath:@"loginResponseTuple" options:NSKeyValueObservingOptionNew block:^(TSNStartupScreenViewModel* observer, TSNAppModel* observed, NSDictionary *change) {
    
        TSNResponseTuple* responseTuple = change[NSKeyValueChangeNewKey];
    
        @strongify(self);
        if([responseTuple.error isError]) {
    
            [TSNAppUtilities showError:(TSNError *)(responseTuple.error) completion:^(OHAlertView *alert, NSInteger buttonIndex) {
    
                if(buttonIndex == alert.firstOtherButtonIndex) {
    
                    // ... process selection
                }
            }];
        } 
    }];