ReactiveCocoa / ReactiveViewModel

Model-View-ViewModel, using ReactiveCocoa
MIT License
1.96k stars 259 forks source link

ViewModel creation boilerplate in table/collectionviews #25

Open zdavison opened 10 years ago

zdavison commented 10 years ago

One problem I've yet to find a solution to using MVVM is where to neatly take care of Model > ViewModel conversion/wrapping.

I tend to have a ViewModel class per each TableViewCell/CollectionViewCell, and hence when I create a datasource that initializes itself with a set of Model objects, I need to do something like the following:

+ (NSArray *)viewModelsFromModels:(NSArray *)models {
  NSMutableArray *array       = [NSMutableArray array];
  for (Model *model in models) {
    ViewModel *viewModel = [[ViewModel alloc] initWithModel:model];
    [array addObject:viewModel];
  }
  return array;
}

This is....kind of gross, and also leads to creeping complexity and difficulty of use whenever you have to unwrap viewmodels to get to their underlying models (eg, when handing off a viewmodel to another class that wants to create it's own viewmodel).

I've thought of a few solutions to this but none really work:

I still haven't come up with a nice way around this, it's my only gripe with MVVM right now. What do you do in your projects?

notxcain commented 10 years ago

You may want to add map: in a category on NSArray. It helps me a lot.

jspahrsummers commented 10 years ago

This belongs in the parent view model, IMO. For example, if you have a ListViewModel, it should be responsible for creating ListItemViewModels out of ListItems.

You can also use -[RACSequence map:] and friends to make the implementation less verbose.

ColinEberhardt commented 10 years ago

I've created a helper class that makes it easy to bind a collection of view model objects to a table view, as follows:

// create a cell template
UINib *nib = [UINib nibWithNibName:@"CETweetTableViewCell" bundle:nil];

// bind the ViewModels 'searchResults' property to a table view
[CETableViewBindingHelper bindingHelperForTableView:self.searchResultsTable
                        sourceSignal:RACObserve(self.viewModel, searchResults)
                        templateCell:nib];

The above binds the viewModel.searchResults property, which is of type NSArray, to the searchResultsTable, rendering each item in the array as a CETweetTableViewCell. Each item in the array is itself a view model which is bound to the respective cell view.

You can read more about it here:

http://www.scottlogic.com/blog/2014/05/11/reactivecocoa-tableview-binding.html

I am currently in the process of updating it to support mutable arrays.

haifengkao commented 9 years ago

I have made a similar helper class called HFTableCollectionBindingHelper. It supports mutable arrays and multiple sections. You can find it here:

https://github.com/haifengkao/HFTableCollectionBindingHelper.