By Kevin Hou
6 minute read
In this post, I will cover the basics of Swift classes, subclasses, and their protocols/delegates. Classes are a foundational piece of computer science and software development. They allow developers to create instances of a certain type of object with pre-defined methods (functions) and instance variables (properties). For example, a class might be a car with an instance variable: wheels = 4 and a method: driveForward(). Here is what it would look like in Swift 3:
1// Declaration
2class Car {
3
4 // Immutable and only accessible within this class
5 // No initial value
6 private let wheels: Int
7
8 // Mutable and accessible outside of this class
9 // Initial value 1
10 public var passengers: Int = 1
11
12 // Called when a new instance of "Car" is created
13 init(wheels: Int) {
14 self.wheels = wheels // Set instance variable
15 }
16
17 // Can be called on an instance of "Car"
18 public func driveForward() -> void {
19 ...
20 }
21}
22
23// Usage
24let porche: Car = Car(wheels: 4)
25print(porche.passengers) // 1
26porche.driveForward() // ...
27
All cars have wheels and they all the ability to drive forward — that is why these become part of the class. Classes are a way of standardizing objects that have similar properties, just different values. This method of creating classes is what allows for modular-code, an essential aspect of computing and engineering.
Subclasses are a way of building on top of existing classes. For example, if we continue with our example of the class Car, a subclass could be Tesla. They share very similar properties, but Tesla's have key features that set them apart from cars. Tesla would inherit all the essential properties and methods from Car, but add on its own special components. Here is an example:
1class Tesla: Car { // Inherit "Car"
2 public func charge() -> void {
3 ...
4 }
5}
6
Any instance of Tesla would still have the same constructor and properties like wheels and driveForward(), but would include a new method charge() unique to instances of type Tesla.
Protocols are a way of enforcing a parent or other class conforms to a set of methods. It's a somewhat complicated topic and it makes the most sense once you've worked with Swift for a little bit, but here are some easy example.
The easiest example of a protocol is when you create a sublcass of a UIViewController. By subclassing, you are adding on top of the existing class UIViewController. You are also given the option to override certain functions like viewDidLoad() and viewDidLayoutSubviews(). These methods are optional protocols in UIViewController made available to you because the delegate — what is responsible for satisfying the protocols — is set to the new subclass.
That's all very confusing, but here's a step by step on how they work and how to create your own class with protocols. It might clear up your confusion.
Create a class that has an event listener.
1class SomeClass {
2 private let button: UIButton = UIButton()
3
4 ...
5
6 private func buttonPressed() {
7 print("I was pressed")
8 }
9}
10
This function, buttonPressed(), will be run every time the button is pressed. This begs the question, how will the UIViewController that contains an instance of SomeClass detect when the button was pressed? It can create an interval timer that checks every 1/10 of a second to see if the button state has changed, but that's neither efficient nor practical. Instead we must use a protocol.
Create a protocol.
1protocol SomeDelegate: class {
2 func buttonWasPressed(someValue: Int) // Passes back a parameter
3}
4
5@objc protocol SomeDelegate: class {
6 @objc optional func buttonWasReleased() // Optional protocol
7}
8
Notice the first protocol passes back a value. This is useful when you want the parent to be able to listen and track when a specific value has changed. You can simply trigger the protocol every time the value is changed and pass the value as the parameter. The second protocol example doesn't take a paramter, but it is optional. This means that the parent class doesn't need to include buttonWasReleased() in order to conform to the protocol SomeDelegate. Notice the use of the @objc tags.
Establish the protocol as an instance variable named delegate.
1class SomeClass {
2 weak var delegate: SomeDelegate?
3}
4
Call the appropriate protocol method in your class to trigger the listener.
1class SomeClass {
2
3 ...
4
5 private func buttonPressed() {
6 print("I was pressed")
7 self.delegate.buttonWasPressed(someValue: 1)
8
9 // For optional protocols
10 self.delegate?.buttonWasReleased?() // Optional so don't force otherwise app will crash
11 }
12}
13
This new line triggers the protocol method buttonWasPressed and passes the value 1 up to the parent. Now we'll cover how to actually use this in your class.
In your UIViewController subclass, add the protocol as a class to conform to.
1class SomeViewController: UIViewController, SomeDelegate {
2 ...
3}
4
Connect or create the instance of the class and attach self as the delegate. This will tell the protocol that this current class will conform to the protocol.
1class SomeViewController: UIViewController, SomeDelegate {
2 override func viewDidLoad() {
3 super.viewDidLoad()
4 let example: SomeClass = SomeClass()
5 example.delegate = self
6 }
7}
8
Add the protocol as a function. If everything was set up correctly, the method should auto-complete.
1class SomeViewController: UIViewController, SomeDelegate {
2
3 ...
4
5 func buttonWasPressed(someValue: Int) {
6 print("Button was pressed with value: \(someValue)")
7 }
8}
9
That's it! Now every time the function buttonPressed() is run, it will run buttonWasPressed() in SomeViewController and pass a value. This is a great way of communicating between a child and parent when some event is triggered by the user.
Hope this tutorial helped! It's been a few months since I did any iOS dev (got caught up in a lot of web dev because of work) so I hope my explanations and code were clear and clean.