简体   繁体   中英

ReactiveCocoa and delegates

I'm trying to communicate with the login service and update the UI the reactive way. The thing is that my login service works with delegates and almost every example I find works with blocks.

I wrote a solution that works, but it seems a bit to clunky, I'm not sure if this is the best way:

LoginViewController :

- (void) viewDidLoad
{
    [super viewDidLoad];

    //Assign the "loginCommand" command to the button. It'll get executed on button pressed and the button is only enabled when the command says so.
    self.entrarBtn.rac_command = self.viewModel.loginCommand;

    //Subscribe and respond to command's successful signals
    @weakify(self);
    [self.viewModel.loginCommand.executionSignals subscribeNext:^(RACSignal *loginSignal) {
        [loginSignal subscribeNext:^(id x) {
            @strongify(self);
            [self.viewPresenter enterMainNavigation];
        }];
    }];

    //Subscribe and respond to command's error signals
    [self.viewModel.loginCommand.errors
     subscribeNext:^(NSError* error) {
         UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"ERROR" message:[NSString stringWithFormat:@"Error: %@", error.localizedDescription]  delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
         [alert show];
     }];    
}

LoginViewModel :

- (id)init
{
    self = [super init];

    if(self) {
        self.loginCommand = [[RACCommand alloc] initWithEnabled:self.enableLoginSignal
                                signalBlock:^RACSignal *(id input) {
                                    return  [self loginSignal];
                                }];
    }

    return self;
}


- (RACSignal *)loginSignal
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        //LOGIN OK
        RACDisposable* loginOKDisposable = [[self rac_signalForSelector:@selector(loginServiceDidReceiveLoginOK)
                                                           fromProtocol:@protocol(LoginServiceDelegate)] subscribeNext:^(id x) {
            PositionGlobalService *positionGlobalService = [PositionGlobalService sharedInstance];
            positionGlobalService.delegate = self;
            [positionGlobalService getPositionGlobal];
        }];

        //GETTING USER INFO DELEGATE THEN SEND THE COMPLETED SIGNAL
        RACDisposable* positionOKDisposable = [[self rac_signalForSelector:@selector(positionGlobalServiceDidReceivePositionGlobal)
                                                              fromProtocol:@protocol(PositionGlobalServiceDelegate)] subscribeNext:^(id x) {
            [subscriber sendNext:nil];
            [subscriber sendCompleted];
        }];
        RACDisposable* positionErrorDisposable = [[self rac_signalForSelector:@selector(positionGlobalServiceDidReceivePositionGlobalError:)
                                                                 fromProtocol:@protocol(PositionGlobalServiceDelegate)] subscribeNext:^(id x) {
            NSError* error = [NSError errorWithDomain:LoginErrorDomain code:LoginErrorGettingUserInfo userInfo:nil];
            [subscriber sendError:error];
        }];

        //ERRORS
        RACDisposable* loginKODisposable = [[self rac_signalForSelector:@selector(loginServiceDidReceiveLoginKO)
                                                           fromProtocol:@protocol(LoginServiceDelegate)] subscribeNext:^(id x) {
            NSError* error = [NSError errorWithDomain:LoginErrorDomain code:LoginErrorKO userInfo:nil];
            [subscriber sendError:error];
        }];


        RACDisposable* deniedDisposable = [[self rac_signalForSelector:@selector(loginServiceDidReceiveLoginKOAccessDenied)
                                                          fromProtocol:@protocol(LoginServiceDelegate)] subscribeNext:^(id x) {
            NSError* error = [NSError errorWithDomain:LoginErrorDomain code:LoginErrorAccessDenied userInfo:nil];
            [subscriber sendError:error];
        }];

        RACDisposable* connectionErrorDisposable = [[self rac_signalForSelector:@selector(loginServiceDidReceiveConnectionError)
                                                                   fromProtocol:@protocol(LoginServiceDelegate)] subscribeNext:^(id x) {
            NSError* error = [NSError errorWithDomain:LoginErrorDomain code:LoginErrorConnectionError userInfo:nil];
            [subscriber sendError:error];
        }];

        RACDisposable* genericErrorDisposable = [[self rac_signalForSelector:@selector(loginServiceDidReceiveGenericError:)
                                                                fromProtocol:@protocol(LoginServiceDelegate)] subscribeNext:^(id x) {
            NSError* error = [NSError errorWithDomain:LoginErrorDomain code:LoginErrorGenericError userInfo:nil];
            [subscriber sendError:error];
        }];


        LoginService *loginService = [LoginService sharedInstance];
        loginService.delegate = self;
        [loginService checkLogin:self.usuario withPassword:self.password documentType:LoginDocumentTypeNIF saveLogin:YES];


        return [RACDisposable disposableWithBlock:^{
            [loginOKDisposable dispose];
            [positionOKDisposable dispose];
            [positionErrorDisposable dispose];
            [loginKODisposable dispose];
            [deniedDisposable dispose];
            [connectionErrorDisposable dispose];
            [genericErrorDisposable dispose];
        }];

    }];
}

As you can see there's a bunch of code that is almost the same for every delegate, that's why I'm unsure whether this is the best way to do it.

Your view looks good, but I have a few suggestions for the model. The main point is that I'd simplify the signals on the LoginService and PositionGlobalService by moving them into the respective classes for those services. You can then merge the errors and create a single signal, eg:

@interface LoginService : SomeSuperclass<LoginServiceDelegate>
- (RACSignal *)loginWithID:(NSString *)userid password:(NSString *password);
@end

@implementation LoginService()

- (RACSignal *)loginWithID:(NSString *)userid password:(NSString *)password {
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        RACDisposable *errorDisposable = [[RACSignal merge:@[[[self rac_signalForSelector:@selector(loginServiceDidReceiveLoginKO) fromProtocol:@protocol(LoginServiceDelegate)] mapReplace:[NSError errorWithDomain:LoginErrorDomain code:LoginErrorKO userInfo:nil]],
                                                             [[self rac_signalForSelector:@selector(loginServiceDidReceiveLoginKOAccessDenied) fromProtocol:@protocol(LoginServiceDelegate)] mapReplace:[NSError errorWithDomain:LoginErrorDomain code:LoginErrorAccessDenied userInfo:nil]],
                                                             [[self rac_signalForSelector:@selector(loginServiceDidReceiveConnectionError) fromProtocol:@protocol(LoginServiceDelegate)] mapReplace:[NSError errorWithDomain:LoginErrorDomain code:LoginErrorConnectionError userInfo:nil]],
                                                             [[self rac_signalForSelector:@selector(loginServiceDidReceiveGenericError) fromProtocol:@protocol(LoginServiceDelegate)] mapReplace:[NSError errorWithDomain:LoginErrorDomain code:LoginErrorGenericError userInfo:nil]]]] subscribeNext:^(id x) {
            [subscriber sendError:x];
        }];
        RACDisposable *loginDisposable = [[self rac_signalForSelector:@selector(loginServiceDidReceiveLoginOK) fromProtocol:@protocol(LoginServiceDelegate)] subscribeNext:^(id x) {
            [subscriber sendNext:x];
            [subscriber sendCompleted];
        }];

        [self checkLogin:userid withPassword:password documentType:LoginDocumentTypeNIF saveLogin:YES];        
        return [RACDisposable disposableWithBlock:^{
            [errorDisposable dispose];
            [loginDisposable dispose];
        }];
    }
}
@end

Then, your login function can become something like this (though I'd probably rename this function since it does two things):

- (RACSignal *)loginSignal
{
    return [[[LoginService sharedInstance] loginWithID:self.usuario password:self.password] then:^RACSignal *{
        return [[PositionGlobalService sharedInstance] getPositionGlobalSignal];
    }];
}];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM