0

For the purpose of making a "more encapsulated" app, I am trying to specify the access levels for my view controllers properties/methods. But the issue is when trying to private a datasource/delegate method, I am getting a compile-time error complaining about it.

For instance, I have two view controllers: ViewControllerA and ViewControllerB, the first one implements the table view datasource method and privateWork() private method, as follows:

class ViewControllerA: UIViewController, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 101
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell")!
        return cell
    }

    private func privateWork() {
        print(#function)
    }
}

And the second view controller has an instance of ViewControllerA -in a method called setupViewControllerA()- as follows:

class ViewControllerB: UIViewController {
    func setupViewControllerA() {
        // in real case, you should get it from its stroyborad,
        // this is only for the purpose of demonstrating the issue...
        let vcA = ViewControllerA()

        vcA.privateWork()
    }
}

When implementing vcA.privateWork() it gives a compile-time error:

'privateWork' is inaccessible due to 'private' protection level

which is so appropriate! I want to prevent the accessing of vcA.privateWork() for other classes.

My issue is: simply, I want to do the same behavior for the datasource methods, so if I tried to implement:

private func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 101
}

I getting a compile time error:

Method 'tableView(_:numberOfRowsInSection:)' must be as accessible as its enclosing type because it matches a requirement in protocol 'UITableViewDataSource'

with a fix suggestion to let private to be internal. It leads to let the following code (in the second view controller):

vcA.tableView(..., numberOfRowsInSection: ...)

to be valid, which is inappropriate, there is no need to let such a datasource method to be accessible from outside of its class.

What should I do for such a case?

Ahmad F
  • 30,560
  • 17
  • 97
  • 143

2 Answers2

2

You don't need to worry about privacy for delegate & datasource method still you have option to create another class that is going to handle your tableview datasource stuff

for example

class IceCreamListDataSource: NSObject, UITableViewDataSource
{
  let dataStore = IceCreamStore()

  // MARK: - Table view data source

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

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

  func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
  {
    let flavor = dataStore.allFlavors()[indexPath.row]
    let cell = tableView.dequeueReusableCellWithIdentifier("IceCreamListCell", forIndexPath: indexPath)
    cell.textLabel?.text = flavor
    return cell
  }
}

Now In your ViewController or UITableViewController class you can use this class as your datasource

class IceCreamListViewController: UITableViewController
{
  let dataSource = IceCreamListDataSource()

  // MARK: - View lifecycle

  override func viewDidLoad()
  {
    super.viewDidLoad()
    tableView.dataSource = dataSource
  }
}

Hope it is helpful

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
1

Make table view adapter that conforms to UITableViewDataSource and UITableViewDelegate and add it as private property to your view controller:

final class TableViewAdapter: NSObject, UITableViewDataSource, UITableViewDelegate {

    // Implementation of protocol methods

}



class ViewControllerA: UIViewController {

    private let adapter = TableViewAdapter()

    @IBOutlet private var tableView: UITableView! {
        didSet {
            tableView.delegate = adapter
            tableView.dataSource = adapter
        }
    }

}
Ilya Kharabet
  • 4,203
  • 3
  • 15
  • 29
  • Forgot about Adapter... Also, you could declare `TableViewAdapter` as `fileprivate` if there is no need to be usable from out of the file... – Ahmad F Feb 13 '18 at 12:36