Generic class when inherited from UICollectionViewDataSource in swift

when I try to create a generic class that implements a UICollectionViewDataSource in swift, it says that my class is not protocol compliant (and once Xcode crashed).

Does this mean that we cannot create a common data provider for the UICollectionView and that we must duplicate the code?

Here is the generic code:

// Enum protocol protocol OptionsEnumProtocol { typealias T static var allValues:[T] {get set} var description: String {get} func iconName() -> String } // enum : list of first available options enum Options: String, OptionsEnumProtocol { typealias T = Options case Color = "Color" case Image = "Image" case Shadow = "Shadow" static var allValues:[Options] = [Color, Image, Shadow] var description: String { return self.rawValue } func iconName() -> String { var returnValue = "" switch(self) { case .Color: returnValue = "color_icon" case .Image: returnValue = "image_icon" case .Shadow: returnValue = "shadow_icon" } return returnValue } } // class to use as the uicollectionview datasource and delegate class OptionsDataProvider<T>: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { private let items = T.allValues func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell let item = self.items[indexPath.row] // Configure the cell cell.iconFileName = item.iconName() cell.labelView.text = item.description return cell } } 

But since this failed, I have to use this non-general form:

 enum Options: String { case Color = "Color" case Image = "Image" case Shadow = "Shadow" static var allValues:[Options] = [Color, Image, Shadow] var description: String { return self.rawValue } func iconName() -> String { var returnValue = "" switch(self) { case .Color: returnValue = "color_icon" case .Image: returnValue = "image_icon" case .Shadow: returnValue = "shadow_icon" } return returnValue } } class OptionsDataProvider: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { private let items = Options.allValues func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell let item = self.items[indexPath.row] // Configure the cell cell.iconFileName = item.iconName() cell.labelView.text = item.description return cell } } 

which oblige me to duplicate the class for each type of enumeration that I have.

Exact error:

enter image description here

+7
generics ios xcode swift uicollectionview
source share
3 answers

You are right, it is impossible to write a clan However, I found a workaround. It does not use enumerations, and you may not find it very useful. However, it does what you want - you get a collection view data source that can be used with various classes, providing the necessary data. Here is the code:

 protocol OptionsProviderProtocol { func allValues() -> [OptionsItem] } class OptionsItem:NSObject { let itemDescription:String let iconName:String init(iconName:String,description:String) { self.itemDescription = description self.iconName = iconName } } // class stores first available options class Options: NSObject, OptionsProviderProtocol { let color = OptionsItem(iconName: "color_icon", description: "Color") let image = OptionsItem(iconName: "image_icon", description: "Image") let shadow = OptionsItem(iconName: "shadow_icon", description: "Shadow") func allValues() -> [OptionsItem] { return [color, image, shadow] } } // class to use as the uicollectionview datasource and delegate class OptionsDataProvider: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { private var items:[OptionsItem] = [] convenience init(optionsProvider:OptionsProviderProtocol) { self.items = optionsProvider.allValues() } func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier(OptionsCellReuseIdentifier, forIndexPath: indexPath) as! GenericIconLabelCell let item = self.items[indexPath.row] // Configure the cell cell.iconFileName = item.iconName() cell.labelView.text = item.description return cell } } 

If you have any questions, please let me know.

+5
source share

When you inherit a protocol, you must implement all the necessary methods. Swift 2 will change that a bit. Perhaps you really want to inherit a class.

+2
source share

I had a similar problem / question when I tried to inherit a Generic class from the NSOperation class. xCode did not give me a compilation error because there were no protocols involved, instead, my override func main() just wasn't called :)

In any case ... If you are following a workaround , then mr. Topal Sergey advised that you can achieve exactly what you want relatively easily.

 class ViewController: UIViewController { @IBOutlet weak var collectionView: UICollectionView? private var defaultDataProvider = OptionsDataProvider<Options>() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. collectionView?.delegate = defaultDataProvider collectionView?.dataSource = defaultDataProvider } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } // Enum protocol protocol OptionsEnumProtocol { static var allValues: [OptionsEnumProtocol] {get set} var description: String {get} func iconName() -> String } // enum : list of first available options enum Options: String, OptionsEnumProtocol { case Color = "Color" case Image = "Image" case Shadow = "Shadow" static var allValues: [OptionsEnumProtocol] = [Color, Image, Shadow] var description: String { return self.rawValue } func iconName() -> String { var returnValue = "" switch(self) { case .Color: returnValue = "color_icon" case .Image: returnValue = "image_icon" case .Shadow: returnValue = "shadow_icon" } return returnValue } } class OptionsDataProviderWrapper: NSObject, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { // MARK: protocols' funcs final func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return wrapperCollectionView(collectionView, numberOfItemsInSection: section) } final func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { return wrapperCollectionView(collectionView, cellForItemAtIndexPath: indexPath) } // MARK: for override func wrapperCollectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 0 } func wrapperCollectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { return UICollectionViewCell() } } class OptionsDataProvider<T: OptionsEnumProtocol>: OptionsDataProviderWrapper { private let items = T.allValues override func wrapperCollectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } override func wrapperCollectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier("reuseId", forIndexPath: indexPath) as! GenericIconLabelCell let item = self.items[indexPath.row] cell.labelView?.text = item.description return cell } } class GenericIconLabelCell: UICollectionViewCell { @IBOutlet weak var labelView: UILabel? } 

The key point here is to create an OptionsDataProviderWrapper that is not generic and implements all your protocols. The only thing he does is he redirects calls to other functions like func wrapperCollectionView...

Now you can inherit your Generic class from this OptionsDataProviderWrapper and override this wrapper.

Note. You should override only those wrapper functions, because the native functions of func collectionView... will not be called in your general subclass like my problem with NSOperation. This is why I have designated final built-in functions.

+1
source share

All Articles