Tables are used to present content in a form of lists using a UIKit element: Table View. Table View is certainly one of the most popular components that can be found in nearly every app!
Table View is a sophisticated UI component that incorporates various sub-elements, the main ones being:
Table header
Table footer
Sections
Cells
Table view acts like a container for all the components within it and also provides a close collaboration with a view controller. This collaboration process is implemented using protocols and delegates.
We briefly touched on these definitions in previous courses. Now, let's make sure we comprehend all the details.
Nuts and bolts of Delegates & Protocols
These two definitions are extremely important in Swift (and many other programming languages) and extensively utilized with many objects, not just Table Views.
Let's start with delegation.
To speed up our experiments, we'll use a Playground project. Go ahead and create one, and name it "Delegation.playground."
Understanding Delegation
Delegation is a software implementation approach that allows one object to act on behalf of another.
Let’s take a car, for example - it needs to be driven. This action is delegated to humans. To implement this approach in code, we need 3 elements:
Car class
Human class
Delegation "link"

What's that link?
The linking part is done via a delegate property that we must declare in the object that will be delegating (A car, in our example). Grab this initial code:
import UIKit
class Car {
var color = UIColor.red
var brand = "Ferrari"
var delegate: AnyObject?
}
class Human {
var height = 170.0
}
The two classes we declared have all the familiar elements. Let's look closer at the delegate property. For now it looks like an ordinary property of optional AnyObject type:
var delegate: AnyObject?
We'll use this property to assign a Human object, which will be responsible for driving.
Wait, can any human drive a car? 😒 👶
Valid concern! Not every human can drive a car. For that matter, we need to make sure the one we are using as a delegate has the required attributes and skills, like has a driving license and is able to drive.
How can we make sure of that? 😅
This is where protocols come into play.
Discovering Protocols
A protocol is a set of requirements. Those requirements ensure that a delegate object can perform delegated actions and has the required attributes.
An object that wants to be a delegate must satisfy those requirements, or in other words, must conform to a protocol:

In our car example, we can declare a Driver protocol, which will be required to have a driver's license as a property, and to be able to drive a car as a method.
Here's what our requirements may look like:
protocol Driver {
var driverLicense: String? { get set }
func drive()
}
As you can see, the declarations are similar to those of a class.
Important points to note here:
Properties of a protocol must specify whether they are read/write or read-only:
var readWriteProperty: String? { get set } // read/write propertyvar readOnlyProperty: String? { get } // read-only propertyMethod declarations don't have a body (no curly brackets), as there's no need for any particular implementation.
There are two categories of protocol member declarations:
Required
Optional
By default, the declared members are required. To declare optional ones, we must use a trick:
@objc protocol ProtocolWithOptionalMembers {
func requiredMethod()
@objc optional func optionalMethod()
}
Back to our Driver example. Two things must happen next:
A delegating object (Car) must specify that only objects that conform to the Driver protocol are allowed to be delegates.
An object that wants to be a driver must declare that it conforms to the Driver protocol and must implement properties and methods required by the protocol.
Declaring delegate protocol
To declare that a delegate object must conform to a particular protocol, we simply change our type declaration:
var delegate: Driver?
A delegate object may be required to conform to more than one protocol, in which case we simply list all multiple protocols in the same declaration:
var delegate: (Driver, Mechanic)?
And we can also separate them by using different delegate properties:
var driverDelegate: Driver?
var mechanicDelegate: Mechanic?
The naming for protocols we've used so far may be confusing. Driver and Mechanic at first glance can refer to classes (or structures). For that matter, it's common to use the Delegate
suffix in the protocol name:
protocol DriverDelegate {
}
protocol MechanicDelegate {
}
class Car {
var driverDelegate: DriverDelegate?
var mechanicDelegate: MechanicDelegate?
}
The next phase: associating delegate objects with protocols!
Declaring conformation to a protocol
To declare that a class conforms to a protocol, we simply list the protocol name after the class name with :
just like we would to list a parent class we want to inherit:
class className: protocolName {
}
And if we used a subclass instead, the full declaration would look like this:
protocol ProtocolName {
}
class ClassName {
}
class SubClassName: ClassName, ProtocolName {
}
So, our Human/Driver conformation will look like this:
class Human: Driver {
var height = 170.0
var driverLicense: String?
func drive() {
}
}
Let's review the above:
we kept our
height
property - a normal property of a classwe declared a required property -
driverLicense
ofDriver
protocolwe declared a required method -
drive()
of Driver protocol.

Now for the fun part! 🎉
Protocol Inheritance
Just like classes, protocols support inheritance! And the declaration is identical to the one for classes:
class SubProtocolName: ProtocolName {
}
So, how can we enhance our drivers?
Using protocol inheritance, we can create a new, very specific, protocol - FerrariDriver
:
protocol FerrariDriver: Driver {
var extremeDriverLicense: String? { get set }
}
And then, do some magic! 💫
1. Declare a special class of super humans that inherit all the elements of the Human
class (a.k.a. subclass Human
class) and conform to a FerrariDriver
protocol! 💪
class SuperHuman: Human, FerrariDriver {
var extremeDriverLicense: String?
}
There're a couple of Important things to point out here:
We did not have to repeat declaration of the original Driver protocol members - the
driverLicense
property anddrive()
method - because we have already declared them in theHuman
class declaration that our newSuperHuman
class inherits.We DID have to declare a property that is required by the
FerrariDriver
protocol:extremeDriverLicense
.And, of course, our subclass inherits the
height
property that we defined in theHuman
class.
2. Another fun variation we can try: let's declare a completely different class bypassing the Human
class inheritance altogether with superSpecies
:
class SuperSpecies: FerrariDriver {
var height = 170.0
var driverLicense: String?
var extremeDriverLicense: String?
func drive() {
}
}
This declaration is very different from the one in the previous variation:
We still needed to declare the new property
extremeDriverLicense
as required byFerrariDriver
protocol.We also had to declare properties and methods required by
Driver
protocol -driverLicense
anddrive()
- because these declarations are no longer inherited.And, we declared its own
height
property, again, because we no longer inherit this property.
Let's put our experimental code snippet together:
import UIKit
protocol Driver {
var driverLicense: String? { get set }
func drive()
}
class Car {
var color = UIColor.red
var brand = "Ferrari"
var delegate: Driver?
}
class Human: Driver {
var height = 170.0
var driverLicense: String?
func drive() {
}
}
Now, back to our Table View! Let’s see what we need to do to make it "collaborate" with a view controller.
Introducing UITableView
Table Views in UIKit are represented by UITableView
class. Let's switch to our storyboard, drag one of those to our app screen, and size it to the size of the screen:

Let's connect it to the code by creating an outlet:

Declaring conformation
To make a TableView object "collaborate" with the view controller we must implement delegation.
First, let's declare that our View Controller conforms to two required protocols:
UITableViewDelegate
UITableViewDataSource
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// ...
}
As soon as we add this conformation code, the Xcode starts complaining, stating that our ViewController does NOT conform to declared protocols:

That's because we haven't implemented all the required methods of those protocols yet. Not to worry!
Let's assign our view controller as a delegate to the table view. To keep the code organized, we'll create a configure()
method and call it in a viewDidLoad
method:
func configure() {
tableView.delegate = self
tableView.dataSource = self
}
override func viewDidLoad() {
super.viewDidLoad()
configure()
}
Declaring required methods
Oh no! The Xcode is still complaining... 😱
We've got two delegates here:
An object that will act as a data source for the table (a.k.a. a content feeder to the table). It needs to implement all the essential methods that will provide the minimum information necessary for the table to populate the table's content.
An object that will act as delegate for table layout and activities as UI element, such as what to do before or after cells display, how to display headers and footers, etc. All methods of this protocol are optional.
Could those two delegates be different objects? 🤔
Those two delegates definitely could be different objects and don't have to be a view controller. However, in the context of MVC architecture it should be some sort of controller. Typically, the same object is used as a delegate for both properties, and it's usually a view controller, as it's meant to manage one of the views: UITableView
.
There are three required methods that a table view data source delegate must implement:
func numberOfSections(in tableView: UITableView) -> Int {
return 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell.init()
}
Let's review them:
numberOfSections
returns a number of sections in the table (we'll learn about table sections in the next chapter);numberOfRowsInSection
returns number of rows for each section using a section index;cellForRowAt
provides a cell object with actual content for each cell by an indexPath.
Let's briefly go over a few new definitions:
Table Section - a group of table rows. A table may have 0 or more sections. Sections are indexed starting from 0 and are of
Int
type.Table Row - a line in a table view that contains ONE cell. Table Views accommodate one cell per row. Rows are indexed starting from 0 and are of
Int
type.Index Path - a composition of section index and row index. This combination is represented by the structure
IndexPath
. It allows us to identify the exact position of a cell in a table.
Let's visualize the above elements (you can test our current app implementation and see how it appears in a simulator):

We will review these components in detail and implement some optional methods of datasource and table view delegated in the following chapter!
Cheating tools: UITableViewController
Just kidding, no cheating here. Do you remember that TableView
is an extremely common element in iOS apps? Because of this, there's a convenient controller element for this: UITableViewController
. UITableViewController
inheritsUIViewController
and automatically implements UITableViewDelegate
andUIDataSourceDelegate
. It also implements some layout adjustments.
Which one to use?
UITableViewController
is good for quick implementation. In more complex projects, however, UITableView
is more commonly used paired withUIViewController
, which implements the necessary delegates.
Let's Recap!
Table View is a container-like UI element that consists of sub-elements:
Table Footer
Table Header
Section Headers
Section Footers
Table Cells
In order to declare a delegate object that conforms to a protocol, we need to declare a variable that conforms to a protocol similar to class type:
var delegate: ProtocolName?
In order to declare conformation to a protocol, classes need to declare inheritance capabilities:
class ClassName: ProtocolName {}
Protocols support inheritance
In order to work with table views, a view controller must conform to the following protocols:
UITableViewDelegate
UITableViewDataSource
UITableViewDataSource requires implementation of the following methods:
numberOfSections
- to supply the number of sections in a tabletableView/numberOfRowsInSection
- to supply the number of rows for each sectiontableView/cellForRowAt/indexPath
- to supply a cell for each row
In the next part of the course, you'll learn how to manage table sub-elements.