© Stefan Kaczmarek, Brad Lees, Gary Bennett 2019
Stefan Kaczmarek, Brad Lees and Gary BennettSwift 5 for Absolute Beginnershttps://doi.org/10.1007/978-1-4842-4868-3_12

12. Protocols and Delegates

Stefan Kaczmarek1 , Brad Lees1 and Gary Bennett2
(1)
Phoenix, AZ, USA
(2)
Scottsdale, AZ, USA
 

Congratulations! You are acquiring the skills to become an iOS developer! However, iOS developers need to understand two additional topics in order to be successful: protocols and delegates. It is not uncommon for new developers to get overwhelmed by these topics, which is why we introduced the foundational topics of the Swift language first. After reading this chapter, you will see that protocols and delegates are really useful and not hard to understand and implement.

Multiple Inheritance

We discussed object inheritance in Chapter 5. In a nutshell, object inheritance means that a child can inherit all the characteristics of its parent, as shown in Figure 12-1.
../images/341013_5_En_12_Chapter/341013_5_En_12_Fig1_HTML.jpg
Figure 12-1.

Typical Swift inheritance

C++, Perl, and Python all have a feature called multiple inheritance , which enables a class to inherit behaviors and features from more than one parent, as shown in Figure 12-2.
../images/341013_5_En_12_Chapter/341013_5_En_12_Fig2_HTML.jpg
Figure 12-2.

Multiple inheritance

Problems can arise with multiple inheritance because it allows for ambiguities to occur. Therefore, Swift does not implement multiple inheritances. Instead, it implements something called a protocol.

Understanding Protocols

Apple defines a protocol as a list of function declarations, unattached to a class definition. A protocol is similar to a class with the exception that a protocol doesn’t provide an implementation for any of the requirements; it describes only what an implementation should look like.

The protocol can be adopted by a class to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.

Protocol Syntax

Protocols are defined like classes are, as shown in Listing 12-1.
protocol RandomNumberGenerator {
    var mustBeSettable: Int { get set }
    var doesNotNeedToBeSettable: Int { get }
    func random() -> Double
}
Listing 12-1.

Protocol Definition

If a class has a superclass, you list the superclass name before any protocols it adopts, followed by a comma, as shown in Listing 12-2.
class MyClass: MySuperclass, RandomNumberGenerator, AnotherProtocol {
    // class definition goes here
}
Listing 12-2.

Protocol Listed After Superclass

The protocol also specifies whether each property must have a gettable or gettable and settable implementation. A gettable property is read-only, whereas a gettable and settable property is not (shown earlier in Listing 12-1).

Properties are always declared as variable properties, prefixed with var. Gettable and settable properties are indicated by { get set } after their type declaration, and gettable properties are indicated by { get }.

Delegation

Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. This design pattern is implemented by defining a protocol that encapsulates the delegated responsibilities. Delegation can be used to respond to a particular action or to retrieve data from an external source without needing to know the underlying type of that source.

Listing 12-3 defines two protocols for use with a random number guessing game.
protocol RandomNumberGame {
    var machine: Machine { get }
    func play()
}
protocol RandomNumberGameDelegate {
    func gameDidStart(game: RandomNumberGame)
    func game(game: RandomNumberGame, didStartNewTurnWithGuess randomGuess: Int)
    func gameDidEnd(game: RandomNumberGame)
}
Listing 12-3

Protocol Definitions

The RandomNumberGame protocol can be adopted by any game that involves random number generating and guessing. The RandomNumberGameDelegate protocol can be adopted by any type of class to track the progress of a RandomNumberGame protocol.

Protocol and Delegation Example

This section shows you how to create a more sophisticated random number guessing app to illustrate how to use protocols and delegation. The app’s home view displays the user’s guess and whether the guess was high, low, or correct, as shown in Figure 12-3.
../images/341013_5_En_12_Chapter/341013_5_En_12_Fig3_HTML.jpg
Figure 12-3.

Guessing game app home view

When the users tap the Guess Random Number button, they are taken to an input screen to enter their guess, as shown in Figure 12-4.
../images/341013_5_En_12_Chapter/341013_5_En_12_Fig4_HTML.jpg
Figure 12-4.

Guessing game app user input view

When the users enter their guess, the delegate method passes the guess back to the home view, and the home view displays the result.

Getting Started

Follow these steps to create the app:
  1. 1.
    Create a new Swift project based on the Single View App template, name it RandomNumberDelegate, and save it, as shown in Figure 12-5.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig5_HTML.jpg
    Figure 12-5.

    Creating the project

     
  2. 2.
    From the Document Outline, select the Main.storyboard, and then select the View Controller Scene. Then select Editor ➤ Embed In ➤ Navigation Controller. This embeds your View Controller in a Navigation Controller and enables you to easily transition back and forth between other View Controllers, as shown in Figure 12-6.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig6_HTML.jpg
    Figure 12-6.

    Embedding the View Controller in a Navigation Controller

     
  3. 3.
    In the View Controller, add two Label objects and two Button objects along with their four respective outlets to control the view, as shown in Figure 12-7. (Be sure to embed the labels and buttons in a horizontally and vertically center-aligned stack view like you did in Chapter 10.)
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig7_HTML.jpg
    Figure 12-7.

    Outlets necessary to control the view

     
  4. 4.

    Next, connect the Action in Listing 12-4 to the playAgainButton.

     
22     // event triggered by playAgainButton
23     @IBAction func playAgain(_ sender: Any) {
24         createRandomNumber()
25         // only show the button when the user guessed the right #
26         playAgainButton.isHidden = true
27         // show the button
28         guessButton.isHidden = false
29         resultLabel.text = ""
30         userGuessLabel.text = "New Game"
31         previousGuess = ""
32     }
Listing 12-4

IBAction Function

  1. 5.

    Add the code in Listing 12-5 for the functions to handle when the user guesses a number and to handle creating a random number.

     
34     // function called from the GuessInputViewController when the user
35     // taps on the Save Button button
36     func userDidFinish(_ controller: GuessInputViewController, guess:  String) {
37         userGuessLabel.text = "The guess was " +  guess
38         previousGuess = guess
39         if let numberGuess = Int(guess) {
40             if (numberGuess > randomNumber) {
41                 resultLabel.text = "Guess too high"
42             } else if (numberGuess < randomNumber) {
43                 resultLabel.text = "Guess too low"
44             } else {
45                 resultLabel.text = "Guess is correct"
46                 //show the play again button
47                 playAgainButton.isHidden = false
48                 //hide the guess again number
49                 guessButton.isHidden = true
50             }
51         }
52
53         // pops the GuessInputViewController off the stack
54         if let navController = self.navigationController {
55             navController.popViewController(animated: true)
56         }
57     }
58
59     // creates the random number
60     func createRandomNumber() {
61         // get a random number between 0-100
62         randomNumber = Int(arc4random_uniform(100))
63         // lets us cheat :)
64         print("The random number is: \(randomNumber)")
65         return
66     }
Listing 12-5

User Guess Delegate Function and createRandomNumber Function

  1. 6.

    Declare and initialize the two variables on lines 12 and 13 in Listing 12-6.

     
11 class ViewController: UIViewController {
12     var previousGuess = ""
13     var randomNumber = 0
14
15     @IBOutlet weak var userGuessLabel: UILabel!
16     @IBOutlet weak var resultLabel: UILabel!
17     @IBOutlet weak var guessButton: UIButton!
18     @IBOutlet weak var playAgainButton: UIButton!
Listing 12-6

Variable Declarations and Initializations

  1. 7.

    Modify the function viewDidLoad() to handle how the view should look when it first appears and create the random number to guess, as shown in Listing 12-7.

     
20     override func viewDidLoad() {
21         super.viewDidLoad()
22         // Do any additional setup after loading the view, typically from a nib.
23
24         createRandomNumber()
25         playAgainButton.isHidden = true
26         resultLabel.text = ""
27     }
Listing 12-7

viewDidLoad Function

  1. 8.
    Now you need to create a view to enable the users to enter their guesses. In the Main.storyboard file, drag a new View Controller next to the home View Controller and add a Label, a Text Field, and a Button in another center-aligned stack view. For the Text Field object, in the Placeholder property, type “Number between 0-100”, as shown in Figure 12-8.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig8_HTML.jpg
    Figure 12-8.

    Creating the Guess View Controller and objects

     
  2. 9.
    You next need to create a class for the Guess Input View Controller. Create a Swift file and save it as GuessInputViewController.swift by selecting File ➤ New ➤ File. Then choose iOS and Cocoa Touch Class and name the class GuessInputViewController as a subclass of UIViewController, as shown in Figure 12-9.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig9_HTML.jpg
    Figure 12-9.

    Creating the GuessInputViewController.swift file

     
  3. 10.
    Let’s associate the GuessInputViewController class with the Guess View Controller created in Step 8. From the Main.storyboard file, select the Guess Input View Controller, select the Identity Inspector, and select or type GuessInputViewController in the Class field, as shown in Figure 12-10.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig10_HTML.jpg
    Figure 12-10.

    Setting the Guess Input View Controller class

     
Now let’s create and connect the actions and outlets in the GuessInputViewController class, as shown in Listing 12-8.
 9 import UIKit
10
11 // protocol used to send data back to the home view controller's userDidFinish
12 protocol GuessDelegate {
13     func userDidFinish(_ controller:GuessInputViewController, guess: String)
14 }
15
16 class GuessInputViewController: UIViewController {
17     var delegate: GuessDelegate?
18     var previousGuess: String = ""
19
20     @IBOutlet weak var guessLabel: UILabel!
21     @IBOutlet weak var guessTextField: UITextField!
22
23     override func viewDidLoad() {
24         super.viewDidLoad()
25
26         // Do any additional setup after loading the view.
27         if(!previousGuess.isEmpty) {
28             guessLabel.text = "Your previous guess was \(previousGuess)"
29         }
30         guessTextField.becomeFirstResponder()
31     }
32
33     @IBAction func saveGuess(_ sender: AnyObject) {
34         if let delegate = delegate, let guessText = guessTextField.text {
35             delegate.userDidFinish(self, guess: guessText)
36         }
37     }
38 }
Listing 12-8

Class Listing

  1. 11.
    You are almost done. You need to connect the scene with a segue. A segue enables you to transition from one scene to another. Control-drag from the Guess Random Number button to the Guess Input View Controller and select Show as the type of Action Segue, as shown in Figure 12-11.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig11_HTML.jpg
    Figure 12-11.

    Creating the segue that transitions scenes when the Guess Random Number button is tapped

     
  2. 12.
    Now you need to give the segue an identifier. Click the segue arrow, select the Attributes Inspector, and name the segue MyGuessSegue, as shown in Figure 12-12.
    ../images/341013_5_En_12_Chapter/341013_5_En_12_Fig12_HTML.jpg
    Figure 12-12.

    Creating the segue identifier

     

Note

Make sure you press Return or Tab after you type the segue identifier. Xcode may not recognize the property change if you don’t press Return.

Now you need to write the code to handle the segue. In the ViewController class, add the code in Listing 12-9.
75     override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
76         if segue.identifier == "MyGuessSegue" {
77             let vc = segue.destination as! GuessInputViewController
78             // passes the previousGuess property to the GuessInputViewController
79             vc.previousGuess = previousGuess
80             vc.delegate = self
81         }
82     }
Listing 12-9

prepareForSegue Method

When the user taps the Guess Random Number button, the segue gets called, and the method prepareForSegue gets called. You first check to see whether it was the MyGuessSegue segue. You then populate the vc variable with the GuessInputViewController.

Lines 79 and 80 pass the previousGuess number and delegate to the GuessInputViewController.
  1. 13.

    If you haven’t added the GuessDelegate delegate to the ViewController class, do it now, as shown in Listing 12-10.

     
11 class ViewController: UIViewController, GuessDelegate {
12     var previousGuess = ""
13     var randomNumber = 0
Listing 12-10

ViewController Class with GuessDelegate Listed

How It Works

Here is how the code works:
  • When the user taps the Guess Random Number link, prepareForSegue is called. See line 75 in Listing 12-9.

  • Because the ViewController conforms to the GuessDelegate (see line 11 in Listing 12-10), you can pass self to the delegate in GuessInputViewController.

  • The GuessInputViewController scene is displayed.

  • When the user guesses a number and taps Save Guess, the saveGuess method is called (see line 33 in Listing 12-8).

  • Since you passed ViewController as the delegate, it can pass the guess back to the ViewController.swift file via the userDidFinish method (see line 35 in Listing 12-8).

  • Now you can determine whether the user guessed the correct answer and pop the GuessInputViewController view from the stack (see line 55 in Listing 12-5).

Summary

This chapter covered why multiple inheritance is not used in Swift and how protocols and delegates work. When you think of delegates, think of helper classes. When your class conforms to a protocol, the delegate’s functions help your class.

You should be familiar with the following terms:
  • Multiple inheritance

  • Protocols

  • Delegates

Exercises

  • Change the random number the computer guesses from 0–100 to 0–50.

  • In the main scene, display how many guesses the user has made trying to guess the random number.

  • In the main scene, display how many games the user has played.