-1

I think I fully understand the concept of delegation, my question is that when we do:

class someViewController : UIViewController, UITableViewDelegate{

}

would it ever be possible that we wouldn't want to set tableView.delegate to self?

If there isn't any chance then why is Xcode forcing us to do some extra work here?

If there is a chance that tableView.delegate is set to something other than self...well what is that? Can you please provide some examples?

mfaani
  • 33,269
  • 19
  • 164
  • 293

3 Answers3

4

When mentioning that tableView.delegate = self or tableView.dataSource = self in the desired ViewController, that's means the ViewController is saying "I am responsible for implementing those delegation/dataSource methods", means that this ViewController (self) is taking care of providing the needed method to let tableView knows how it should looks/behaves.

Referring to your questions:

would it ever be possible that we wouldn't want to set tableView.delegate to self?

Actually it's possible, but this causes to let the tableView appears as an empty tableView (no rows in it), because no one is telling it about how it should looks/behave.

If there is a chance that tableView.delegate is set to something other than self...well what is that? Can you please provide some examples?

Yes you can, tableView.dataSource/delegate not necessary to be assigned to the same Viewcontroller that contains this tableView (but I find it more readable and understandable).

For example:

In the following code snippets, I assigning the dataSource of the tableView to another separated class (which is not even a UIViewController) on a different .swift file and it completely works fine:

import UIKit

// ViewController File
class ViewController: UIViewController {
    var handler: Handler!

    @IBOutlet weak var tableView: UITableView!
    override func viewDidLoad() {
        super.viewDidLoad()

        handler = Handler()
        tableView.dataSource = handler
    }
}

Handler Class:

import UIKit

class Handler:NSObject, UITableViewDataSource {
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("myCell")

        cell?.textLabel?.text = "row #\(indexPath.row + 1)"

        return cell!
    }
}

The output works fine as it should.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • Akhi, so by doing such, you make your code more modular? And your not leaking your viewModel to the controller? – mfaani Dec 21 '16 at 20:46
  • 1
    Not necessary, for me, I think that letting the container ViewController handles the dataSource/Delegation is more natural and readable. It might be case(s) that doing such a thing is -somehow- better, but I'm not pretty sure about it :) – Ahmad F Dec 21 '16 at 20:51
  • perhaps it would make it easier for you to test the handler as the datasource,delegate than the viewController itself. And you can also re-use it elsewhere – mfaani Dec 21 '16 at 20:53
  • This might be useful when you to build an app sing a different architectural pattern (Not the regular MVC), at the end it is up you, developer. To give you a little trick: you can create an extension to your ViewController and let it responsible for handling the dataSource and Delegate methods. – Ahmad F Dec 21 '16 at 20:57
  • @AhmadF so if we follow your approach can we also define delegate and datasource into different different classes also. – Sumit Jangra Nov 27 '17 at 09:41
  • @SumitDhariwal Yes you can, it is not necessary the class that contains the object with delegate/datasource to implement them itself. – Ahmad F Nov 27 '17 at 10:11
1

would it ever be possible that we wouldn't want to set tableView.delegate to self

Yes, if you would implement datasource and delegate in it's own class.

why is Xcode forcing us to do some extra work here?

to allow as much freedom in our architectures as possible.


An example with separate classes for datasource and delegate.

class TableViewDatasource: NSObject, UITableViewDataSource {
    // implement datasource
}

class TableViewDelegate : NSObject, UITableViewDelegate {
    // implement delegate
}

class ViewController: UIViewController {

     IBOutlet weak var tableView: UITableView! {
         didSet {
              tableView.dataSource = tableViewDatasource
              tableView.delegate = tableViewDelegate
         }
     }

     let tableViewDatasource = TableViewDatasource()
     let tableViewDelegate = TableViewDelegate()

}

This allows higher reusability and favours composition over inheritance, if you'd allow view controllers to hold different implementations of delegate and datasource.

You deal with smaller classes and those are easier to test and maintain.

It is even possible to design complete apps, that don't need any view controller subclasses.


datasource/delegate design can be as sophisticated as you like.

As an example I want to show you a new project of mine: TaCoPopulator. It is a framework to populate table view and collection views transparently by splitting it up in distinct task to follow the SOLID Principles:

import UIKit

class CollectionViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!
    private var datasource: ViewControllerDataSource<UICollectionView>?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.datasource = ViewControllerDataSource(with: collectionView)
    }
}

import UIKit

class TableViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    private var datasource: ViewControllerDataSource<UITableView>?

    override func viewDidLoad() {
        super.viewDidLoad()
        self.datasource = ViewControllerDataSource(with: tableView)
    }
}

import TaCoPopulator


class IntDataProvider: SectionDataProvider<Int> {

    override init(reuseIdentifer: @escaping (Int, IndexPath) -> String) {
        super.init(reuseIdentifer: reuseIdentifer)
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
            self.provideElements([1,2,3,4,5,6,7,8,9])
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
                self.provideElements(self.elements() + [10, 11, 12, 13, 14, 15])
            }
        }
    }
}

import TaCoPopulator


class ViewControllerDataSource<TableOrCollectionView: PopulatorView> {
    init(with populatorView: PopulatorView) {
        self.populatorView = populatorView
        setup()
    }

    var intSelected: ((Int, IndexPath) -> Void)?
    var stringSelected: ((String, IndexPath) -> Void)?

    let dp1 = IntDataProvider {
        _ in return "Cell"
    }

    let dp2 = StringDataProvider {
        _ in return "Cell"
    }


    weak var populatorView: PopulatorView?
    var populator: Populator<MyViewPopulator>?

    func setup(){
        dp1.selected = {
            [weak self] element, indexPath in
            self?.intSelected?(element, indexPath)
        }

        dp2.selected = {
            [weak self] element, indexPath in
            self?.stringSelected?(element, indexPath)
        }

        let collectionViewCellConfig: (Any, TextCollectionViewCell, IndexPath) -> TextCollectionViewCell  = {
            element, cell, _ in
            cell.textLabel?.text = "\(element)"
            return cell
        }

        let tableViewViewCellConfig: (Any, UITableViewCell, IndexPath) -> UITableViewCell  = {
            element, cell, _ in
            cell.textLabel?.text = "\(element)"
            return cell
        }

        if  let populatorView  = populatorView as? UICollectionView {
            let  section1factory = SectionCellsFactory<Int, TextCollectionViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: collectionViewCellConfig)
            let  section2factory = SectionCellsFactory<String, TextCollectionViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: collectionViewCellConfig)
            self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
        }

        if  let populatorView  = populatorView as? UITableView {
            let section1factory = SectionCellsFactory<Int, UITableViewCell>(parentView: populatorView, provider: dp1, cellConfigurator: tableViewViewCellConfig)
            let section2factory = SectionCellsFactory<String, UITableViewCell>(parentView: populatorView, provider: dp2, cellConfigurator: tableViewViewCellConfig)
            self.populator = Populator(with: populatorView, sectionCellModelsFactories: [section1factory, section2factory])
        }
    }

}
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
1

would it ever be possible that we wouldn't want to set tableView.delegate to self?

What's tableView.delegate? Your class someViewController is not a subclass of UITableViewController (and does not have a tableView property). It does not even know that you handle a table view.

The fact that your type declaration conforms to UITableViewDelegate does not make it obvious on which actual table view instance it should be set.

If there is a chance that tableView.delegate is set to something other than self...well what is that?

Often times it's wise to split up functionality into multiple types to reduce the complexity of the view controller. Your someViewController might have a viewModel property that handles all thing regarding the table view.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • ok, but for whatever tableView property that I have...wouldn't I want its delegate to be set to `self`? Is there a chance that for any number of `tableView`s inside `someViewController` I would want to set its delegate to something other than `self`? can you add some code to your 2nd answer? – mfaani Dec 21 '16 at 20:33
  • @Honey What exactly is `self` for you? `self` is something that depends on current context. I would understand your question if you were asking about `UIViewController` but not when you are asking about `self.` `self` means *nothing*. – Sulthan Dec 21 '16 at 20:36
  • @Sulthan It seems that I'm confusing something...Isn't it obvious from the question, `self` is referring to `SomeViewController`. Or in general whatever class that is conforming to the protocol – mfaani Dec 21 '16 at 20:38