miho
miho

Reputation: 12085

NSInvalidArgumentException when putting UITableViewDataSource in a generic class

I tried to encapsluate my tables data source into an own class called ArrayDataSource which looks like the following lines of code:

public class ArrayDataSource<T>: NSObject, UITableViewDataSource {
    var items: [[T]]

    // ...

    public func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return self.items.count
    }

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

    // ...

}

This complies quite fine and no complaints about missing implementations are made, but when using it like

self.tableViewDataSource = ArrayDataSource<Stop>(items: stopsInSections, cellReuseIdentifier: "StopCell", configureClosure: { view, stop in /* ... */ })
// yes, self.tableViewDataSource is a strong reference, since self.tableView.dataSource doesn't seem to be
self.tableView.dataSource = self.tableViewDataSource

But when I use it, my app crashes with the following debug output:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_TtC12WannWieWohin15ArrayDataSource00000000146B4108 tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x146b4250'

Any ideas what I could have made wrong?

Upvotes: 0

Views: 127

Answers (2)

Jon N
Jon N

Reputation: 1489

Unfortunately you can't use a generic swift class with Cocoa - the dynamic method calls fail.

One approach you can do at the moment is have a property on your generic datasource that returns a non-generic class that wraps your generic one.

There is an implementation of this approach here: https://github.com/jonnermut/thesarvo/blob/master/thesarvo_iphone_2.0/thesarvo/thesarvo/Model/SectionedDataSource.swift

Upvotes: 0

bluedome
bluedome

Reputation: 2459

Removing generics from class declaration, your code will work.
But this looks like a bug(of generics or respondsToSelector:?).

// with Generics
public class ArrayDataSource<T>: NSObject, UITableViewDataSource { ... }

var ds = ArrayDataSource<String>()
ds.respondsToSelector("tableView:numberOfRowsInSection:") // false

// without Generics
public class ArrayDataSource2: NSObject, UITableViewDataSource { ... }

var ds2 = ArrayDataSource2()
ds2.respondsToSelector("tableView:numberOfRowsInSection:") // true

Upvotes: 2

Related Questions