6

I have been playing around with protocol extensions and I have a problem. Maybe what I want to achieve can’t be done. I have this playground:

//: Playground - noun: a place where people can play

import UIKit

protocol ArrayContainer {
    typealias T
    var array: [T] { get }
}

class MyViewController: UIViewController, ArrayContainer, UITableViewDataSource {
    typealias T = String
    var array = ["I am", "an Array"] 
}

extension UITableViewDataSource where Self: ArrayContainer {

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

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

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        // Whatever
        return UITableViewCell()
    }   
}

This is what I have and what I want:

  • I have a protocol ​ArrayContainer​ that just has a typealias and a array which contains objects of this typealias type
  • I have a protocol extension of ​UITableViewDataSource​ to be used when the class conforms with the ​ArrayController​ protocol. This simply returns the number of items of the array as number of rows. The cellForRowAtIndexPath method is not well implemented, but it is not the problem.
  • I have a ​UIViewController​ subclass called ​MyViewController​ which implements both protocols.

The problem is that the compiler complains because MyViewController doesn’t conforms with UITableViewDataSource but, as far as i know, it should be covered by the UITableViewDataSource extension. Am I missing something here? or maybe Objective-C protocols can not be extended?

manueGE
  • 1,169
  • 11
  • 14
  • 1
    The problem may be related to http://stackoverflow.com/questions/32542362/swift-2-0-uitextfielddelegate-protocol-extension-not-working – ABakerSmith Sep 15 '15 at 09:55
  • I'm pretty sure the issue is because protocol extensions aren't accessible via Objective-C, and the `UITableViewDataSource` implementation needs to be ObjC accessible. – Logan Sep 16 '15 at 14:24
  • Yes, I think this is the issue, but is it documented anywhere? – manueGE Sep 16 '15 at 14:34
  • Protocol extensions in Swift don't currently support dynamic dispatch. – Christopher Mann Nov 06 '15 at 19:18
  • @ManueGE I've added a practical implementation below. If you think this would work for you, would you mind marking it as an accepted answer? I'm not sure if asking about this is bad SO etiquette so please let me know. – swift taylor Jun 13 '16 at 22:04
  • well that's annoying... hope apple gets their s_ together and updates these protocols. – user160917 Nov 29 '16 at 20:28

1 Answers1

5

I know it's a bit late to respond, and you may not even be looking for this answer, but I just came across this exact issue and needed a real world "solution". You can implement the UITableViewDataSource methods in the class and then immediately hand off the work to the protocol extension like the example below. If swift makes improvements that no longer require this, it's simple to change back to the code in your original post.

//: Playground - noun: a place where people can play

import UIKit

protocol ArrayContainer {
    associatedtype T
    var array: [T] { get }
}

class MyViewController: UIViewController, ArrayContainer, UITableViewDataSource {
    typealias T = String
    var array = ["I am", "an Array"]

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return self.internal_numberOfSectionsInTableView(tableView)
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.internal_tableView(tableView, numberOfRowsInSection: section)
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        return self.internal_tableView(tableView, cellForRowAtIndexPath: indexPath)
    }
}

extension UITableViewDataSource where Self: ArrayContainer {

    func internal_numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

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

    func internal_tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        // Whatever
        return UITableViewCell()
    }   
}
swift taylor
  • 610
  • 5
  • 10