Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

ViewModel creation boilerplate in table/collectionviews #25

Open
zdavison opened this issue Jun 11, 2014 · 4 comments
Open

ViewModel creation boilerplate in table/collectionviews #25

zdavison opened this issue Jun 11, 2014 · 4 comments
Labels

Comments

@zdavison
Copy link

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:

  • Create categories on the underlying model that creates a viewmodel for whatever our current context is. This a: gets messy and b: doesn't let us do any caching/lazy computation of viewmodel properties (which is a nice bonus), it also could have performance hits since we'd be creating/destroying these objects frequently.
  • Have every cell store a viewModel and have the datasource update the underlying model property of the viewModel. This also means we don't get any nice caching of our viewModel properties, and limits us to functions that expose/transform the underlying models properties in realtime as they are called.
  • Tucking away this code into a generic macro like MVVM_ADAPT(collection, targetClass). This is sort of okay.

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
Copy link

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

@jspahrsummers
Copy link
Member

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
Copy link

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
Copy link

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants