Using Protocols to Provide Templates for Functionality - Using Components and Subcomponents - Swift For Dummies (2015)

Swift For Dummies (2015)

Part IV. Using Components and Subcomponents

Chapter 18. Using Protocols to Provide Templates for Functionality

In This Chapter

arrow Working with protocols

arrow Conforming classes, structures, and enumerations to a protocol

arrow Using protocols with a UITableViewController

In earlier chapters, when the subject of protocols came up, I promised to talk more about them later. This chapter, then, is the “later” I was referring to. It provides a formal description of protocols and delegates (the implementers of protocols) along with examples of their use — including perhaps the most common use of protocols and delegates you may encounter: table views with their data sources and delegate protocols.

You can find more details about protocols in the Swift documentation on developer.apple.com. Protocols can be used to implement complex architectures, but in and of themselves, they’re quite simple, as you’ll see in this chapter.

icon tip This chapter refers to the Locatapp example code shown in Chapter 4 and in more detail in Chapter 9.

Understanding Protocols

Protocols declare a set of methods and properties (as well as a few other syntax elements such as operators and subscripts) that are collected together and provided with a name. The structures of protocols and delegates allow you to implement additional functionality in classes without subclassing them; protocols also let you add functionality and properties to structures and enumerations. When a protocol exists, here’s what can happen:

· A protocol declares the methods and properties: Protocols declare them, but they don’t implement them. (Protocols and subscripts are not discussed in this chapter in order to focus on the more common properties and methods.)

· A class, structure, or enumeration then adopts the protocol: This means that a class/structure/enumeration must implement the methods and properties of the protocol.

· A class, structure, or enumeration that adopts a protocol (or a class inherits from a class that adopts the protocol) conforms to the protocol: The subclass that inherits from a class that adopts a protocol must implement the required methods and properties of that protocol unless the superclass which actually adopts the protocol already does so.

A protocol has no connection to the objects that adopt or conform to it.

In most cases, a protocol can be adopted by a number of different classes, structures, and enumerations.

The Cocoa and Cocoa Touch frameworks frequently use protocols and delegates. This use of protocols and delegates is called the delegation design pattern, and is one of a number of design patterns that occur frequently in Cocoa and Cocoa Touch (others include key-value observing, target-action, and error reporting).

In the delegation pattern, the instance of a class declares a property that is a delegate (and often it is named delegate, but this is not required). When messages are sent to the class (Objective-C) or when a function of the class is called (Swift), the class passes the message or call to its delegate which actually implements the code.

The syntax of protocols is different in Objective-C and Swift, but from here on, this chapter refers only to the Swift syntax. It’s important to note, however, that protocols are inherently usable across both languages. The protocol declaration in Swift or Objective-C can be implemented in either language. Furthermore, in a single app, it’s entirely possible for the same protocol to be implemented in different contexts by both Objective-C and Swift. Thus, protocols are one of the important elements in making the two languages work together.

Experimenting with Protocols

This section provides a brief example of a simple protocol and how you can use it with a class, structure, or enumeration. The example used here can be downloaded as described in the Introduction. The full code is shown in Listing 18-1 at the end of this section; it is shown in a playground in Figures 18-1 and 18-2.

image

Figure 18-1: Experiment-ing with protocols (top).

image

Figure 18-2: Experiment-ing with protocols (bottom).

Declaring a protocol

A protocol is introduced by the keyword protocol. It contains the declarations that must be implemented by the types that adopt the protocol. The most common elements of a protocol are methods and properties. The example shown in this section uses a single property, but multiple properties in a protocol are permitted. You can also have properties in protocols that are adopted by classes and structures. Functions (methods) can be adopted by all the types that can adopt protocols (that is, by classes, structures, and enumerations), and I’ve included examples of each here so you can compare the usage.

A protocol is named just as other Swift elements are named. The basic protocol declaration looks like this:

protocol MyProtocol {
}

As with classes, structures, and enumerations, the name is capitalized. In fact, most Swift objects except for properties and functions are capitalized, and they use internal camelCase (capital letters for each of the embedded words except the first).

If the protocol contains a function that must be implemented by types that adopt it, the protocol declaration might look like this:

protocol MyProtocol {
  func myFunc () -> String
}

An object conforming to MyProtocol must implement myFunc.

icon tip In order to focus on the syntax of protocols, the examples in this section show instance methods rather than type methods, which are comparable to class methods in Objective-C. Instance methods are most commonly used.

Protocols can inherit from one another. Thus, you can declare a pair of protocols as follows:

protocol MyProtocol {
  func myFunc () -> String
}

protocol MyProtocol2: MyProtocol {
  func myFunc2 () -> String
}

An object conforming to MyProtocol must implement myFunc. An object conforming to MyProtocol2 must implement myFunc2, but it must also implement myFunc because MyProtocol2 inherits from MyProtocol. If MyProtocol2 stands on its own (that is, if it does not inherit from MyProtocol), objects that conform to either MyProtocol or MyProtocol2 must implement myFunc — but this is the same function in both protocols. This is okay. Just don’t think you’d have to implement it twice.

icon tip The following sections allow you to experiment with protocols in order to show you the inheritance of protocols. Don’t use a structure in which an identically-named function (or property) is used in multiple protocols unless you really mean it. (An init function would be a good example of the proper use of duplicate function names.)

If you want your protocol to be adopted only by classes, use the keyword class in the list, as in the following:

Protocol MyProtocol: class, MyProtocolToInheritFrom

Note that the class is the keyword you use: It’s not the name of a class.

Adopting and conforming a class, structure, or enumeration to a protocol

Any of the major types (classes, structures, and enumerations) can adopt protocols. You can create a protocol that is adopted by any of them, or you can specify that it is adoptable only by a class. Here are examples of conforming to the basic protocol (MyProtocol) shown in the previous section.

Reference and value types

Structures and enumerations are value types, whereas classes are reference types. Although you may not have come across it before, this concept is used in languages other than Swift. A reference type is a reference (often a pointer) to an object that is allocated on the heap (an area of memory for the app to use). When you pass a reference to an instance of a class as a parameter to a function, behind the scenes you pass the pointer to the instance, and that single instance is what you access through the pointer. If you make a change in one context, it affects references to the instance in other contexts because there is only one instance with several references pointing to it.

When you use an enumeration or a structure, however, you pass it as a value in a parameter. The data is copied into a location on the stack rather than being shared from a common location in the heap. This means that changes you make to a structure or enumeration in a function are not applied to a shared instance.

Conforming a class to a protocol

Many of the protocols used in the Cocoa and Cocoa Touch frameworks are adopted by classes in the frameworks in part because in Objective-C, protocols are typically used only with classes. You’ll be able to move beyond classes, but, when you’re writing code that uses the frameworks, you’ll frequently have to write code that conforms to protocols for classes.

Here are a few guidelines to conforming classes to protocols:

· You specify that a class adopts a protocol in its declaration, as in the following:

class MyClass: MyProtocol {

· If you adopt more than one protocol, separate them with commas, as in the following:

class MyClass: MyProtocol, MyProtocol2 {

· If your class is a subclass of another class, its superclass appears first in the list, as in the following:

class MyClass: MySuperclass, MyProtocol2

Remember that Swift does not support multiple inheritance, so there can only be one superclass (or none). You can add additional protocols to the list if necessary.

icon tip If your class is a subclass of a class that adopts a protocol, you must conform to that protocol in your own class unless the superclass already conforms to it. You don’t specify this inherited adopted protocol in your own declaration. You’ll see an example of this in the following section with UITableView and its protocols.

Having indicated that your class adopts a protocol, you must now implement all of the required properties and methods. (It’s possible to signify that some methods and properties are optional, but the default setting is that they are all required.)

Here is an example of a class that conforms to a protocol. Note that myFunc is required by the protocol, whereas intVal is a class property that has nothing to do with the protocol:

class MyClass: MyProtocol {
  func myFunc () -> String {
    return "Protocol 1"
  }
  var intVal: Int = 0
}

You can create a variable (with var) or constant (with let) that contains an instance of the class with this code:

var myClass: MyClass = MyClass()

You can then access the class’s intVal instance property as well as the protocol’s required method myFunc:

myClass.intVal = 25
myClass.myFunc()

At this point, you make no distinction between the methods and properties required by the protocol and those that are simply part of the class.

Conforming a structure to a protocol

A structure (struct) adopts a protocol in the same way as a class does — with code like this:

struct MyStruct: MyProtocol {
  func myFunc () -> String {
    return "Protocol 2"
  }
  var intVal: Int = 0
  var One = 1
  var Two = 2
}

You can declare a variable that uses the structure. You can then access the members of the structure as well as the function that’s required by the protocol:

var myStruct: MyStruct = MyStruct()
myStruct.intVal = 15
myStruct.myFunc()

Conforming an enumeration to a protocol

Enumerations follow the same basic design. You can declare an enumeration that adopts a protocol alongside its own data, as in the following code:

enum MyEnum: MyProtocol {
  func myFunc () -> String {
    return "Protocol 3"
  }
  case One
  case Two
  case Three
  case Four
}

Then, use the enumeration with a variable in your code:

var myEnum: MyEnum = MyEnum.Two
myEnum.myFunc ()

Listing 18-1 shows these samples put together. You can download the code as described in the Introduction and experiment with it in other ways.

Listing 18-1: Experimenting with Protocols

// Playground - noun: a place where people can play

protocol MyProtocol {
  func myFunc () -> String
}

class MyClass: MyProtocol {
  func myFunc () -> String {
    return "Protocol 1"
  }
  var intVal: Int = 0
}

var myClass: MyClass = MyClass()
myClass.intVal = 25
myClass.myFunc()

struct MyStruct: MyProtocol {
  func myFunc () -> String {
    return "Protocol 2"
  }
  var intVal: Int = 0
  var One = 1
  var Two = 2
}

enum MyEnum: MyProtocol {
  func myFunc () -> String {
    return "Protocol 3"
  }
  case One
  case Two
  case Three
  case Four
}

var myStruct: MyStruct = MyStruct()
myStruct.intVal = 15
myStruct.myFunc()

var myEnum: MyEnum = MyEnum.Two
myEnum.myFunc ()

Exploring Protocols and a UITableViewController

The Master-Detail Application template, and thus the Locatapp, uses a UITableViewController to display the master list of events. Table views are very common in Cocoa Touch and, on the Mac, in Cocoa. A great deal of the work is done for you already, and two protocols play a key role in the structure.

Table views on both OS X and iOS integrate very well with Core Data; the combination is frequently used as it is in Locatapp. This section explores the protocol side of things so that you’ll see how to use UITableViewController in your own code.

This book focuses on Swift and not the frameworks, but this excursion into the frameworks helps to familiarize you with real-world uses of protocols and delegates.

The Master-Detail Application template contains the protocols that you need. They are already configured for you in the template, but you should examine what you have.

The basic architecture is that UITableViewController is designed to work together with two protocols: a data source (a class that adopts the UITableViewDataSource) and a delegate (a class that adopts the UITableViewDelegate protocol).

The data source provides the functionality involved with the table and its data. Its required methods specify the number of rows and sections in the table along with their titles and headers; its methods also manage editing of the table structure (moving and deleting rows).

The table view delegate protocol handles the appearance of the table: indentations, row heights, selection, editing table content (as opposed to the structure which is handled by the data source protocol), and taps in a cell, and the appearance of the contents of a cell.

Looking at delegation and protocols

The delegate protocol provides the user interface. Its methods manage selections and the editing of content.

Together, UITableViewController and its protocols (UITableViewDataSource and UITableViewDelegate) provide a powerful set of functionality that is easy for you to use and customize. This could all have been written as one gigantic class, but by splitting it into a main class and two protocols, it is easier to maintain (and, for many people, easier to understand).

The common implementation in many examples and Xcode templates basically reassembles the base class and the protocols into one large object.

Setting delegates in Interface Builder

You can assign an instance of a class to the delegate property in UITableView. You don’t have to worry about that because first of all, that’s a framework/interface issue and this book focuses on the Swift language, and second of all, it’s already done for you in most of the templates and examples. Here’s a review of how it’s done.

1. Open Main.storyboard in Xcode using Interface Builder (the default editor for that file).

2. Open the document outline if needed.

3. Open Master Scene, the Master controller (yellow circle), and then Table View.

There will be two Master Scene sections in the document outline. Open each one and then look at the Master controller in the yellow circle. One has a navigation arrow, and the other has a table list image. You want the table list image (which is shown in Figure 18-3.)

When you look at the Table View in Main.storyboard using Interface Builder in Xcode and shown in Figure 18-3, you’ll see that the two declarations shown previously (delegate and dataSource) show up as outlets. They are connected to the Master object in the document outline rather than being connected in your code.

4. Select Master (the yellow circle).

As you can see in Figure 18-4, when you select Master in the document outline of Interface Builder, you can see the other side of the connection: the two referencing outlets (dataSource and delegate) are connected to Table View. (Consult the detail windows shown in Figures 18-3and 18-4 to see which one you're looking at.)

When you make connections like these in Interface Builder, you can always look at it from both sides. It is this connection in the template that associates the table view with Master.

5. With Master selected, look at the Identity inspector in the utilities area (shown at the back right of Figure 18-4 ).

As you see, Master is an instance of MasterViewController. You can see that by highlighting it and looking at Quick Help. There you’ll see that MasterViewController is a subclass of UITableViewController.

6. Look in MasterViewController.swift to see its declaration in the template (and, thus, in Locatapp).

Figure 18-5 shows the declaration.

There’s no reference to the protocols. How do they get into the code?

The answer is that MasterViewController is a subclass of UITableViewController (you can see that in Figure 18-5).

7. Highlight UITableViewController in the declaration shown in Figure 18-5 and open Quick Help.

There you can find a link to its reference.

8. If you click on the reference to the reference documentation for UITableViewController.

You’ll see the documentation for UITableViewController as shown in Figure 18-6.

The answer is at the top in the Conforms to section: UITableViewController conforms to both UITableViewDataSource and to UITableViewDelegate. This means that it or subclasses of it must conform to those protocols. (It really doesn’t matter whether it is the original class —UITableViewController in this case — or a subclass such as MasterViewController that conforms. The required methods and properties must be present when you build the project and run it.

image

Figure 18-3: The delegate and data source are set in the template.

image

Figure 18-4: See the referencing outlets from the Master side.

image

Figure 18-5: Looking at the MasterViewController declaration.

image

Figure 18-6: The UITableViewControl-ler documentation.