Search code examples
objective-cmvvmreactive-cocoaraccommand

Using RACCommand with MVVM pattern, sending parameters to ViewModel


I'm using ReactiveCocoa framework at my app for the power using MVVM design pattern.

So for every Controller, I have a ViewModel. and the Controller is binded to his ViewModel.

UIButton binding will look like so:

@implementation HomeController

-(void) bindViewModel {
 self.viewHeader.buttonSide.rac_command = self.viewModel.execiteFullPortfolio;
}

It all works well, But when i would like to pass parameters to the ViewModel, I'm not sure what is the right way to do so...

Say I have a UICollectionView of Stocks, and every click on a specific stock, I would like to navigate to thats stocks profile page. That logic should be done at the ViewModel, But how do i get the stock passed with the RACCommand?

What I'm currently doing is :

@implementation HomeController
-(void) bindViewModel {
 __unsafe_unretained HomeController *weakSelf = self;
self.viewPortfolioPusherView.scrollGainView.blockSelect = ^ (STStock *stock){
        weakSelf.viewModel.selectedStock = stock;
        [weakSelf.viewModel.executeGoToStock execute:[RACSignal empty]];
    };

}


@implementation HomeViewModel
-(void) initialization {
  self.executeGoToStock = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf moveToSelectedStock];
        });
        return [RACSignal empty];
    }];
}
-(void) moveToSelectedStock {
    [self stockProfileControllerLazy];
    self.stockProfileController.stock = self.selectedStock;
    [Navigator pushController:self.stockProfileController fromController:[self.delegate controller]];
}

I'm sure this is not best practice! So I'm asking, What is??

Thanks .


Solution

  • Why not just pass the STStock instance into the call to execute on the command, rather than an empty signal?

    [weakSelf.viewModel.executeGoToStock execute:stock];
    

    Then:

    self.executeGoToStock = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(STStock *stock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf moveToSelectedStock:stock];
            });
            return [RACSignal empty];
    }];
    

    You obviously need to modify moveToSelectedStock to take a parameter as well. However, I'd go a bit further an implement an RACCommand on your Navigator that does that. Furthermore, I'd make a separate view model for an instance of STStock rather than a collection. So, when you select a stock, it might look something a little more like this:

    StockViewModel *viewModel = [[StockViewModel alloc] initWithStock:stock];
    [[Navigator pushViewModel] execute:viewModel];
    

    This obviously omits a few details. For example, my navigator maps view model classes to controller classes. When a view model is pushed, it creates the corresponding controller, and binds the view model to it.