Lighter View Controller Data Sources

Inspired by the concept of Lighter View Controllers, neatly explained by Chris Eidhof on, I came up with a way of separating the view controller, data source and network code.

If you haven’t read the aforementioned article I suggest you do it and then come back to this page.

I started out with an ArrayDataSource like Chris suggested:

self.dataSource = [[ArrayDataSource alloc] initWithItems:photos

tableView.dataSource = self.dataSource;

My experience using ArrayDataSource #

After some time three issues popped up:

  1. Most of my view controllers had collection/table views that present results from a web service call. ArrayDataSource only supports arrays, so I had to fetch and parse from the server before creating the data source.
  2. I needed pagination. That means my datasource should be able to grow.
  3. I needed some modification features: move, replace, delete, etc…

I could easily handle the first issue by creating the data source after the network call finished. The view controller would be the usual place for that code, but I wanted to keep it light.

The second issue could be handled by replacing the array with a bigger one each time I had to add a page.

The last was the trickiest. I was starting to have a lot of code outside the data source only to mutate it. That code should go inside the data source.

Enter RemoteDataSource #

@interface RemoteDataSource : NSObject

- (instancetype)initWithServiceURL:(NSURL *)serviceURL
                  pageParameterName:(NSString *)pageParameterName 

- (void)fetchWithCompletionBlock:(void (^)(NSError *error))block;

- (BOOL)fetchMoreWithCompletionBlock:(void (^)(NSError *error, NSArray *indexPaths))block;

- (void)cancel;

id<RemoteDataSourceParser> parser = ...

self.dataSource = [[RemoteDataSource alloc] initWithServiceURL:url 

tableView.dataSource = self.dataSource;

As you can see I decided to separate the parsing code from the fetching code. The idea is that you might have different service results for each calls. For example, one for shops, one for locations, etc…

The parser protocol is also pretty straightforward:

@protocol RemoteDataSourceParserProtocol

- (NSArray *)parseData:(NSData *)data error:(NSError **)error;


As you can see, easy as pie to implement. Here’s an example:

@implementation ShopsRemoteParser

- (NSArray *)parseData:(NSData *)data error:(NSError **)error pageValue:(inout NSUInteger *)pageValue {
    NSError *err;

    id o = [NSJSONSerialization JSONObjectWithData:data options:0 error:&err];

    if (err) {
        if (error) *error = err;
        return nil;

    if (![o isKindOfClass:[NSArray class]]) {
        if (error) *error = [NSError errorWithDomain:@"RemoteDataSourceParsingErrorDomain" code:0 userInfo:nil];
        return nil;

    NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[o count]];

    for (id dict in o) {
        if (![dict isKindOfClass:[NSDictionary class]]) continue;
        id obj = [[Shop alloc] initWithInfo:dict];
        if (obj) [arr addObject:obj];

    return [arr copy];


What should a data source do? #

As the name implies, a data source should provide data to other object, frequently a view or view controller. So should it know how to create views? No it shouldn’t. That’s why Chris added that configureCellBlock.

Just a quick note. I would love to know the reason behind tableView:cellForRowAtIndexPath:. I mean, why did Apple decided it should be the data source’s job to create cells?

Let’s imagine you have a collection view of Flickr photos, as you scroll down the next page of items is appended to the bottom. When you tap one item you present a new view controller with a full screen collection view that shows the same photos. As you (horizontally) scroll to the end of this collection view the next page should be appended to the right.

You want both collection views to share the same data source. It’s the same data. And you want to reflect the same state on both as the user goes back and forth. But if the data source owns the cell identifier and configuration block you cannot share it between collection views. The cells are completely different.

A lighter RemoteDataSource #

To be able to pass data sources around they can’t have any cell configuration code. So I refactored it into UIDataSource and RemoteDataSource. And while at it I also introduced AbstractDataSource.


UIDataSource conforms to UITableViewDataSource and UICollectionViewDataSource.

@interface UIDataSource : NSObject <UICollectionViewDataSource, UITableViewDataSource>

@property(nonatomic, strong) AbstractDataSource *dataSource;
@property(nonatomic, copy, readonly) NSString *cellIdentifier;

- (instancetype)initWithDataSource:(AbstractDataSource *)dataSource cellIdentifier:(NSString *)cellIdentifier configureCellBlock:(void (^)(id cell, id object, NSIndexPath *indexPath))block;


Now each view controller can have a UIDataSource and the inner AbstractDataSource can be shared.

Note: This concept came to me after reading how NSTableViewDataSource works. Basically the data source only provides an object. It’s the cell’s job to display it.

Mutating the data source #

The AbstractDataSource class is actually just a definition of the needed methods and a placeholder implementation. A bunch of your typical NSAssert(@"implement this on a subclass"); methods.

It has methods for adding/removing/moving/replacing items, all with empty implementations.

@interface AbstractDataSource : NSObject <UICollectionViewDataSource, UITableViewDataSource>

- (id)objectForKeyedSubscript:(NSIndexPath *)indexPath;
- (NSIndexPath *)indexPathForObject:(id)object;
- (NSUInteger)count;

- (void)insertObject:(id)object atIndexPath:(NSIndexPath *)indexPath;
- (BOOL)deleteItemAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)moveItemFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath;
- (void)replaceObjectAtIndexPath:(NSIndexPath *)indexPath withObject:(id)object;


One last thing you might notice is that AbstractDataSource still conforms to UICollectionViewDataSource and UITableViewDataSource. I figured that the data source needs to be able to define the number of sections and rows. If you have a DictionaryDataSource with { title : array } pairs for example.

It doesn’t make sense for a RemoteDataSource to be modified, and the code to remove an item from ArrayDataSource will certainly be different from SQLTableDataSource.

The final setup #

The way I described those classes is a simplification of the actual code I ended up with, but I think it’s enough to explain the concept. You can now add caching, errors, new data sources, etc…

This is just an idea. Everybody has a way of organizing code. This happens to be mine at the moment. The best one I found so far for these types of applications.

You can find a sample project on Github.

I can’t recommend enough by the way.


Now read this

NSKeyedUnarchiver and Swift

Too many years using Objective-C (or any another language really) will give you a sixth sense. Let me show you an example: something in the back of your head should shout at the following code. NSString *string = [self stringOrNil];... Continue →