Kevvv
Kevvv

Reputation: 4023

How does UITableView work with UITableViewDataSource?

I'm new to Swift and I'm trying to understand how tableView.dataSource = self relates to the functions within the extension:

class ChatViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var messageTextfield: UITextField!

    var messages: [Message] = [
        Message(sender: "email", body: "Hey!" ),
        Message(sender: "email", body: "Hello!" ),
        Message(sender: "email", body: "What's up!" ),
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        title = K.appName
        navigationItem.hidesBackButton = true
        tableView.dataSource = self
    }

    @IBAction func sendPressed(_ sender: UIButton) {
    }


    @IBAction func logOutPressed(_ sender: UIBarButtonItem) {
        do {
          try Auth.auth().signOut()
            navigationController?.popToRootViewController(animated: true)
        } catch let signOutError as NSError {
          print ("Error signing out: %@", signOutError)
        }
    }
}


extension ChatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print(section)
        return messages.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath)
        cell.textLabel?.text = messages[indexPath.row].body
        return cell
    }
}

tableView.dataSource is a variable from the storyboard, which is different from the two functions defined in the extension called tableView, which just happens to have the same name. The variable tableView is being passed as the first argument to both of the functions tableView and not being used within the definition.

What is the relationship between the variable tableView and the function tableView and how the variable is being used in the function even though it's not in the definition of the function? And what does tableView.dataSource = self do?

Upvotes: 0

Views: 151

Answers (1)

Sweeper
Sweeper

Reputation: 272685

Many classes in UIKit uses this "data source" pattern to populate themselves with data. Other than UITableView, there's also UICollectionView, UIPickerView and UIPageViewControllerDataSource, just to name a few. What is said in this answer can also be applied to those classes too.


Instead of making you give it all the data all at once, UITableView asks for the data only when it needs. This is because sometimes the data could be, say, on the Internet, fetching them all at once could take a long time, couldn't it?

Okay, so who should the table view ask for the data? The dataSource! How does the table view know that its data source can answer its questions? By ensuring that the data source conforms to the UITableViewDataSource protocol, which defines a bunch of methods.

Therefore, tableView.dataSource = self is saying:

Hey tableView, if you want to ask for the data, just ask self (the ChatViewController)!

By implementing UITableViewDataSource, we are saying that this class is able to provide answers to table view's questions.

How do we actually provide the answers? By implementing the data source methods!

// this is asking:
// for section number "section" the table view "tableView", how many rows does it have?
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    print(section)
    return messages.count // we answer: it has as many rows as "messages.count"
}

// this is asking:
// for the row at the index path "indexPath", in the table view "tableView",
// what UITableViewCell should I display?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath)
    cell.textLabel?.text = messages[indexPath.row].body
    return cell // we answer: you should display this cell!
}

Note that although the above two data source methods do indeed have the name tableView, it is more useful to refer to them as numberOfRowsInSection and cellForRowAt, and this is also what people often do.

Less importantly (for this question), in these methods, tableView refers to the parameter, not the property declared in your class. To refer to the property declared in your class, you need self.tableView.

The table view will call these methods whenever appropriate, also supplying the parameters. For example, cellForRowAt will be called when it needs to display a new cell.

A few misconceptions of yours that I spotted:

tableView.dataSource is a variable from the storyboard

The dataSource property is declared in UITableView. You are merely able to see it in the storyboard (or more accurately, Xcode's Interface Builder). dataSource is not really "from" the storyboard.

The variable tableView is being passed as the first argument to both of the functions tableView and not being used within the definition.

This tableView is the name of the parameter. You are not passing anything. Remember that you are declaring these methods, and you are not supposed to pass parameters. The caller - the table view - is.

Furthermore, you are using the tableView parameter in cellForRowAt, because the parameter hides the tableView property declared in ChatViewController.

Upvotes: 2

Related Questions