Isuru Isuru - 1 month ago 12
iOS Question

Separating Data Source to another class in Swift

I'm trying to keep my view controllers clean as described in this article objc.io Issue #1 Lighter View Controllers. I tested this method in Objective-C and it works fine. I have a separate class which implements

UITableViewDataSource
methods.

#import "TableDataSource.h"

@interface TableDataSource()

@property (nonatomic, strong) NSArray *items;
@property (nonatomic, strong) NSString *cellIdentifier;

@end

@implementation TableDataSource

- (id)initWithItems:(NSArray *)items cellIdentifier:(NSString *)cellIdentifier {
self = [super init];
if (self) {
self.items = items;
self.cellIdentifier = cellIdentifier;
}
return self;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath];

cell.textLabel.text = self.items[indexPath.row];

return cell;
}

@end


From the tableview controller, all I have to do is instantiate a instance of this class and set it as the tableview's data source and it works perfectly.

self.dataSource = [[TableDataSource alloc] initWithItems:@[@"One", @"Two", @"Three"] cellIdentifier:@"Cell"];
self.tableView.dataSource = self.dataSource;


Now I'm trying to do the same in Swift. First here's my code. Its pretty much of a translation of the Objective-C code above.

import Foundation
import UIKit

public class TableDataSource: NSObject, UITableViewDataSource {

var items: [AnyObject]
var cellIdentifier: String

init(items: [AnyObject]!, cellIdentifier: String!) {
self.items = items
self.cellIdentifier = cellIdentifier

super.init()
}

public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}

public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as UITableViewCell

cell.textLabel?.text = items[indexPath.row] as? String

return cell
}

}


And I call it like this.

let dataSource = TableDataSource(items: ["One", "Two", "Three"], cellIdentifier: "Cell")
tableView.dataSource = dataSource


But the app crashes with the following error.

-[NSConcreteNotification tableView:numberOfRowsInSection:]: unrecognized selector sent to instance

I checked the
init
method of
TableDataSource
and the items and the cell identifier gets passed fine. I had to declare the
UITableViewDataSource
methods
public
and remove the
override
keyword otherwise it would give compile time errors.

I'm clueless on what's going wrong here. Can anyone please help me out?

Thank you.

Answer

Create a property for data source and use it with your tableview.

class ViewController: UIViewController {

  @IBOutlet weak var tableView: UITableView!
  var dataSource:TableDataSource!

  override func viewDidLoad() {
    super.viewDidLoad()

    dataSource = TableDataSource(items: ["One", "Two", "Three"], cellIdentifier: "Cell")

   tableView.dataSource = dataSource

  }
}