Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Swift Protocol-Oriented Programming
Swift Protocol-Oriented Programming

Swift Protocol-Oriented Programming: Increase productivity and build faster applications with Swift 5 , Fourth Edition

Arrow left icon
Profile Icon Jon Hoffman
Arrow right icon
$32.99
Paperback Jun 2019 224 pages 4th Edition
eBook
$15.99 $22.99
Paperback
$32.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Jon Hoffman
Arrow right icon
$32.99
Paperback Jun 2019 224 pages 4th Edition
eBook
$15.99 $22.99
Paperback
$32.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$15.99 $22.99
Paperback
$32.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

Swift Protocol-Oriented Programming

Starting with the Protocol

This book is all about protocol-oriented programming. When Apple announced Swift 2 at the World Wide Developers Conference (WWDC) in 2015, they also declared that Swift was the world's first protocol-oriented programming language. From its name, we may assume that protocol-oriented programming is all about the protocol; however, this would be incorrect. Protocol-oriented programming is about so much more than just the protocol; it's actually a new way of not only writing applications, but also how we think about application design.

In this chapter, you will learn about the following topics:

  • How to define property and functional requirements within a protocol
  • How to use protocol inheritance and composition
  • How to use a protocol as a type
  • What is polymorphism?
  • How to use associated types with protocols
  • How to implement the delegation pattern with protocols
  • How to design type requirements with protocols

If you are coming from an object-oriented programming background, you may be familiar with the interface. In the object-oriented world, for most languages, the interface is a type that contains method and property signatures but does not contain implementation details. An interface can be considered a contract where any type that conforms to the interface must implement the required functionality defined within it. Most object-oriented developers rarely use interfaces as the focal point for their application design unless they are working with a framework similar to the Open Service Gateway Initiative (OSGi) framework. In protocol-oriented programming, the protocol is the focal point of your design.

When we are designing an application in an object-oriented way, we usually begin by focusing on the class hierarchy and how the objects interact. The object is a data structure that contains information about the attributes of the object in the form of properties, and the actions performed by or to the object in the form of methods. We cannot create an object without a blueprint that tells the application what attributes and actions to expect from the object. In most object-oriented languages, this blueprint comes in the form of a class. A class is a construct that allows us to encapsulate the properties and actions of an object into a single type.

Designing an application in a protocol-oriented way is significantly different from designing it in an object-oriented way. Rather than starting with the class hierarchy, protocol-oriented design says that we should start with the protocol. While protocol- oriented design is about so much more than just the protocol, we can think of the protocol as its backbone. After all, it would be pretty hard to have protocol-oriented programming without the protocol.

A protocol in Swift is similar to the interface in object-oriented languages, where the protocol acts as a contract that defines the methods, properties, and other requirements that are needed by our types to perform their tasks. We say that the protocol acts as a contract because any type that conforms to the protocol promises to implement the requirements defined by the protocol itself. If a type says that it conforms to a protocol and it doesn't implement all the functionality defined by the protocol, we will get a compile-time error and the project will not compile. In Swift, any class, structure, or enumeration can conform to a protocol.

We've just mentioned that the protocol is similar to the interface. Don't be fooled by this comparison because even though the interface and the protocol are similar, protocols in Swift are actually far more powerful than the interface in most object-oriented languages. As you read this book, you will find out how powerful Swift protocols can be.

Most modern object-oriented programming languages implement their standard library with a class hierarchy; however, the basis of Swift's standard library is the protocol (http://swiftdoc.org). Therefore, not only does Apple recommend that we use the protocol-oriented programming paradigm in our applications, but to also use it in the Swift standard library.

With the protocol being the basis of the Swift standard library and also the backbone of the protocol-oriented programming paradigm, it is very important that we fully understand what the protocol is and how we can use it. In this chapter, we will cover not only the basics of the protocol but also provide an understanding on how it can be used in application design.

Protocol syntax

In this section, we will look at how to define a protocol and how to add requirements to it. This will give us a basic understanding of the protocol. The rest of this chapter will build on this understanding.

Defining a protocol

The syntax we use to define a protocol is very similar to the syntax that's used to define a class, structure, or enumeration. The following example shows the syntax that's used to define a protocol:

protocol MyProtocol { 
    //protocol definition here 
} 

To define the protocol, we use the protocol keyword, followed by the name of the protocol. We then put the requirements, which our protocol defines, between curly brackets. Custom types can state that they conform to a particular protocol by placing the name of the protocol after the type's name, separated by a colon. The following example shows how we would define that a structure conforms to a protocol:

struct  MyStruct:  MyProtocol  { 
    //Structure  implementation  here 
} 

A type can also conform to multiple protocols. We list the multiple protocols that the type conforms to by separating them with commas:

struct MyStruct: MyProtocol, AnotherProtocol, ThirdProtocol { 
    //Structure implementation here 
} 

Having a type conform to multiple protocols is a very important concept within protocol- oriented programming, as we will see later in this chapter and throughout this book. This concept is known as protocol composition. Now, let's see how we would add property requirements to our protocol.

Property requirements

A protocol can require that the conforming types provide certain properties with specified names and types. The protocol doesn't say whether the property should be a stored or computed property because the implementation details are left up to the conforming types.

When defining a property within a protocol, we must specify whether the property is a read-only or a read-write property by using the get and set keywords. We also need to specify the property's type since we cannot use type inference in a protocol. Let's look at how we would define properties within a protocol by creating a protocol named FullName, as shown in the following example:

protocol FullName { 
    var firstName: String {get  set} 
    var lastName: String {get  set} 
} 

In this example, we define two properties named firstName and lastName, which are read-write properties. Any type that conforms to this protocol must implement both of these properties. If we wanted to define the property as read-only, we would define it using only the get keyword, as shown in the following code:

var readOnly: String {get} 

It is possible to define static properties by using the static keyword, as shown in the following example:

static var typeProperty: String {get} 

Static properties are properties that are owned by the type and shared by all instances. This means that if one instance changes the value of this property, then the value changes for all instances. We will look at how to use static instances more when we look at the singleton pattern.

Now, let's look at how we would add method requirements to our protocol.

Method requirements

A protocol can require that the conforming types provide specific methods. These methods are defined within the protocol exactly as we define them within a class or structure, but without the curly brackets and method body. We can define that these methods are instance or type methods using the static keyword. Adding default values to the method's parameters is not allowed when defining the method within a protocol.

Let's add a method named getFullName() to the FullName protocol:

 protocol FullName  { 
    var firstName: String {get  set} 
    var lastName: String {get  set} 
 
    func getFullName() -> String 
} 

The FullName protocol now requires one method named getFullName() and two read- write properties named firstName and lastName.

For value types, such as the structure, if we intend for a method to modify the instances that it belongs to, we must prefix the method definition with the mutating keyword. This keyword indicates that the method is allowed to modify the instance it belongs to. The following example shows how to use the mutating keyword with a method definition:

mutating func changeName() 

If we mark a method requirement as mutating, we don't need to write the mutating keyword for that method when we adopt the protocol with a reference (class) type. The mutating keyword is only used with value (structures or enumerations) types.

Optional requirements

There are times when we want protocols to define optional requirements. An optional requirement is a method or property that doesn't need to be implemented. To use optional requirements, we need to start off by marking the protocol with the @objc attribute.

It is important to note that only classes can adopt protocols that use the @objc attribute. Structures and enumerations cannot adopt these protocols.

To mark a property or method as optional, we use the optional keyword. The following example shows how we would create both an optional property and also an optional method:

@objc protocol Phone { 
    var phoneNumber: String {get  set} 
    @objc optional var emailAddress: String {get  set} 
    func dialNumber() 
    @objc optional func getEmail() 
} 

If we are using the @objc attribute, as shown in the previous example, we cannot use the mutating keyword because it isn't valid for classes. Now, let's explore how protocol inheritance works.

Protocol inheritance

Protocols can inherit requirements from one or more additional protocols and then add additional requirements. The following code shows the syntax for protocol inheritance:

protocol ProtocolThree: ProtocolOne, ProtocolTwo { 
    //Add requirements here 
} 

The syntax for protocol inheritance is very similar to class inheritance in Swift, except that we are able to inherit from more than one protocol. Let's see how protocol inheritance works. We will use the FullName protocol that we defined earlier and create a new protocol named Person:

 protocol Person: FullName { 
    var age: Int {get set} 
} 

Now, when we create a type that conforms to the Person protocol, we must implement the requirements defined in the Person protocol, as well as the requirements defined in the FullName protocol. As an example, we could define a Student structure that conforms to the Person protocol, as shown in the following code:

 struct Student: Person { 
    var firstName = ""  
    var lastName = ""  
    var age = 0 
 
    func getFullName() -> String { 
        return "\(firstName) \(lastName)" 
    } 
}  

Note that in the Student structure, we implemented the requirements defined in both the FullName and Person protocols. However, the only protocol specified in the structure definition was the Person protocol. We only needed to list the Person protocol because it inherited all the requirements from the FullName protocol.

Now, let's look at a very important concept in the protocol-oriented programming paradigm: protocol composition.

Protocol composition

Protocol composition lets our types adopt multiple protocols. This is a major advantage that we get when we use protocols rather than a class hierarchy because classes, in Swift and other single-inheritance languages, can only inherit from one superclass. The syntax for protocol composition is the same as the syntax for protocol inheritance that we just saw. The following example shows how we would use protocol composition:

struct MyStruct: ProtocolOne, ProtocolTwo, Protocolthree { 
    //implementation  here 
}  

Protocol composition allows us to break our requirements into many smaller components rather than inheriting all the requirements from a single protocol or single superclass. This allows our type families to grow in width rather than height, which means we avoid creating bloated types that contain requirements that are not needed by all conforming types. Protocol composition may seem like a very simple concept, but it is a concept that is essential to protocol-oriented programming. Let's look at an example of protocol composition so that we can see the advantage we get from using it.

Let's say that we have the class hierarchy that's shown in the following diagram:

In this class hierarchy, we have a base class named Athlete. The Athlete base class then has two subclasses named Amateur and Pro. These classes are used depending on whether the athlete is an amateur athlete or a pro athlete. An amateur athlete may be a collegiate athlete, and we would need to store information such as which school they go to and their GPA. A pro athlete is one that gets paid for playing the game. For the pro athletes, we would need to store information such as what team they play for and their salary.

In this example, things get a little messy under the Amateur and Pro classes. As we can see, we have separate football player classes under both the Amateur and Pro classes (the AmFootballPlayer and ProFootballPlayer classes). We also have separate baseball classes under both the Amateur and Pro classes (the AmBaseballPlayer and ProBaseballPlayer classes). This means we need to have a lot of duplicate code between these classes.

With protocol composition, instead of having a class hierarchy where our subclasses inherit all the functionality from a single superclass, we have a collection of protocols that we can mix and match in our types:

We can then use one or more of these protocols as needed for our types. For example, we can create an AmFootballPlayer structure that conforms to the Athlete, Amateur, and FootballPlayer protocols. We could then create the ProFootballPlayer structure that conforms to the Athlete, Pro, and FootballPlayer protocols. This allows us to be very specific about the requirements for our types and only adopt the requirements that we need.

From a pure protocol point of view, this example may not make a lot of sense right now because protocols only define the requirements; however, in Chapter 3, Extensions, we will look at how protocol extensions can be used to implement these types with minimal duplicate code.

One word of warning: if you find yourself creating numerous protocols that only contain one or two requirements in them, then you are probably making your protocols too granular. This will lead to a design that is hard to maintain and manage.

Now, let's look at how a protocol is a full-fledged type in Swift.

Using protocols as a type

Even though no functionality is implemented in a protocol, they are still considered a full-fledged type in the Swift programming language, and can mostly be used like any other type. What this means is that we can use protocols as parameters or return types for a function. We can also use them as the type for variables, constants, and collections. Let's look at some examples of this. For the next few examples, we will use the following Person protocol:

protocol Person { 
    var firstName: String {get set}  
    var lastName: String {get set}  
    var birthDate: Date {get set}  
    var profession: String {get} 
    init (firstName: String, lastName: String, birthDate: Date) 
} 

In this Person protocol, we define four properties and one initializer.

For this first example, we will show you how to use a protocol as a parameter and return type for a function, method, or initializer. Within the function itself, we also use Person as the type for a variable:

func updatePerson(person: Person) -> Person  { 
    var newPerson: Person 
    // Code to update person goes here  
    return newPerson 
} 

We can also use protocols as the type to store in a collection, as shown in the following example:

var personArray = [Person]() 
var personDict = [String:  Person]()  

We can use the instance of any type that conforms to our protocol anywhere that the protocol type is required. Let's assume that we have two types named SwiftProgrammer and FootballPlayer that conform to the Person protocol. We can then use them as follows:

var myPerson: Person 
 
myPerson = SwiftProgrammer(firstName: "Jon", lastName: "Hoffman", 
birthDate: birthDateProgrammer) myPerson = FootballPlayer(firstName: "Dan", lastName: "Marino",
birthdate: birthDatePlayer)

As we saw earlier, we can use the Person protocol as the type for an array, which means that we can populate the array with instances of any type that conforms to the Person protocol. The following is an example of this (note that the bDateProgrammer and bDatePlayer variables are instances of the date type that would represent the birth date of the individual):

var programmer = SwiftProgrammer(firstName: "Jon", lastName: "Hoffman", 
birthDate: bDateProgrammer) var player = FootballPlayer(firstName: "Dan", lastName: "Marino",
birthDate: bDatePlayer) var people: [Person] = [] people.append(programmer) people.append(player)

What we are seeing in these last couple of examples is a form of polymorphism. To use protocols to their fullest potential, we need to understand what polymorphism is.

Polymorphism with protocols

The word polymorphism comes from the Greek roots poly (meaning many) and morphe (meaning form). In programming languages, polymorphism is a single interface for multiple types (many forms). There are two reasons to learn the meaning of the word polymorphism. The first reason is that using such a fancy word can make you sound very intelligent in casual conversation. The second reason is that polymorphism provides one of the most useful programming techniques not only in object-oriented programming, but also in protocol-oriented programming.

Polymorphism lets us interact with multiple types through a single uniform interface. In the object-oriented programming world, the single uniform interface usually comes from a superclass, while in the protocol-oriented programming world, that single interface usually comes from a protocol.

In the previous section, we saw two examples of polymorphism with Swift. The first example was as follows:

var myPerson: Person 
 
myPerson = SwiftProgrammer(firstName: "Jon", lastName: "Hoffman", 
birthDate: birthDateProgrammer) myPerson = FootballPlayer(firstName: "Dan", lastName: "Marino",
birthdate: birthDatePlayer)

In this example, we had a single variable of the Person type. Polymorphism allowed us to set the variable to instances of any type that conforms to the Person protocol, such as the SwiftProgrammer or FootballPlayer types.

The other example of polymorphism was as follows:

 
var programmer = SwiftProgrammer(firstName: "Jon", lastName:  "Hoffman", 
birthDate: bDateProgrammer) var player = FootballPlayer(firstName: "Dan", lastName: "Marino",
birthDate: bDatePlayer) var people: [Person] = [] people.append(programmer) people.append(player)

In this example, we created an array of Person types. Polymorphism allowed us to add instances of any types that conform to the Person protocol to this array.

When we access an instance of a type through a single uniform interface, as we just saw, we are unable to access type-specific functionality. As an example, if we had a property in the FootballPlayer type that records the age of the player, we would be unable to access that property because it is not defined in the People protocol.

If we do need to access type-specific functionality, we can use type casting.

Type casting with protocols

Type casting is a way to check the type of an instance and/or to treat the instance as a specified type. In Swift, we use the is keyword to check whether an instance is of a specific type and the as keyword to treat an instance as a specific type.

The following example shows how we would use the is keyword:

if person is SwiftProgrammer { 
    print("(person.firstName) is a Swift Programmer") 
} 

In this example, the conditional statement returns true if the Person instance is of the SwiftProgrammer type, or false if it isn't. We can use the where statement in combination with the is keyword to filter an array to only return instances of a specific type. In the following example, we filter an array that contains instances of the Person protocol and have it only return those elements of the array that are instances of the SwiftProgrammer type:

for person in people where person is SwiftProgrammer { 
    print("(person.firstName) is a Swift Programmer") 
}  

Now, let's look at how we would cast an instance to a specific type. To do this, we can use the as keyword. Since the cast can fail if the instance is not of the specified type, the as keyword comes in two forms: as? and as!. With the as? form, if the casting fails, it returns a nil. With the as! form, if the casting fails, a runtime error is thrown; therefore, it is recommended to use the as? form unless we are absolutely sure of the instance type or if we perform a check of the instance type prior to doing the cast. The following example shows how we would use the as? keyword to attempt to cast an instance of a variable to the SwiftProgammer type:

if let _ = person as? SwiftProgrammer { 
    print("(person.firstName) is a Swift Programmer") 
} 

Since the as? keyword returns an optional, we could use optional binding to perform the cast.

Now, let's see how we can use associated types with protocols.

Associated types with protocols

When defining a protocol, there are times when it is useful to define one or more associated types. An associated type gives us a placeholder name that we can use within the protocol in place of a type. The actual type to use for the associated type is not defined until the protocol is adopted. The associated type basically says: we don't know the exact type to use; therefore, when a type adopts this protocol, it will define it. As an example, if we were to define a protocol for a queue, we would want the type that adopts the protocol to define the instance types that the queue contains rather than the protocol.

To define an associated type, we use the associatedtype keyword. Let's see how we can use associated types within a protocol. In this example, we will illustrate the Queue protocol, which will define the requirements that are needed to implement a queue:

protocol Queue  { 
    associatedtype QueueType 
    mutating func addItem(item: QueueType) 
    mutating func getItem() -> QueueType?  
    func count() -> Int 
} 
 

In this protocol, we define one associated type named QueueType. We then use this associated type twice within the protocol. First, we use it as the parameter type for the addItem() method, and then we use it again when we define the return type of the getItem() method as an optional type.

Any type that implements the Queue protocol must specify the type to use for the QueueType placeholder, and must also ensure that only items of that type are used where the protocol requires the QueueType placeholder.

Let's look at how to implement Queue in a non-generic class called IntQueue. This class will implement the Queue protocol using the integer type:

struct IntQueue: Queue  { 
    var items = [Int]() 
    mutating func addItem(item: Int) { 
        items.append(item) 
    } 
    mutating func getItem() -> Int?  { 
        if items.count > 0 { 
            return items.remove(at:  0) 
        } 
        else { 
            return  nil 
        } 
    } 
    func count() -> Int { 
        return items.count 
    } 
} 

As we can see in the IntQueue structure, we use the integer type for both the parameter type of the addItem() method and the return type of the getItem() method. In this example, we implemented the Queue protocol in a non-generic way. Generics in Swift allow us to define the type to use at runtime rather than compile time. We will show you how to use associated types with generics in Chapter 4, Generics.

Now that we have explored protocols in some detail, let's look at how we can use them in the real world. In the next section, we will look at how to use protocols to implement the delegation design pattern.

Delegation

Delegation is used extensively within the Cocoa and Cocoa Touch frameworks. The delegation pattern is a very simple but powerful pattern where an instance of one type acts on behalf of another instance. The instance that is doing the delegating keeps a reference to the delegate instance, and then, when an action happens, the delegating instance calls the delegate to perform the intended function. Sounds confusing? It really isn't.

This design pattern is implemented in Swift by creating a protocol that defines the delegates' responsibilities. The type that conforms to the protocol, known as the delegate, will adopt this protocol, guaranteeing that it will provide the functionality that's defined by the protocol.

For the example in this section, we will have a structure named Person. This structure will contain two properties of the String type, named firstName and lastName. It will also have a third property that will store the delegate instance. When either the firstName or lastName properties are set, we will call a method in the delegate instance that will display the full name. Since the Person structure is delegating the responsibility for displaying the name to another instance, it doesn't need to know or care how the name is being displayed. Therefore, the full name could be displayed in a console window or in a UILabel; alternatively, the message may be ignored altogether.

Let's start off by looking at the protocol that defines the delegate's responsibilities. We will name this delegate DisplayNameDelegate:

protocol DisplayNameDelegate { 
    func displayName(name: String) 
}  

In the DisplayNameDelegate protocol, we define one method that the delegate needs to implement named displayName(). It is assumed that, within this method, the delegate will somehow display the name; however, it is not required. The only requirement is that the delegate implements this method.

Now, let's look at the Person structure that uses the delegate:

struct Person  { 
    var displayNameDelegate: DisplayNameDelegate 
 
    var firstName = "" { 
        didSet { 
            displayNameDelegate.displayName(name: getFullName()) 
        } 
    } 
    var lastName =  "" { 
        didSet { 
            displayNameDelegate.displayName(name: getFullName()) 
        } 
    } 
 
    init(displayNameDelegate: DisplayNameDelegate) { 
        self.displayNameDelegate = displayNameDelegate 
    } 
 
    func getFullName() -> String { 
        return "\(firstName) \(lastName)" 
    } 
} 

In the Person structure, we start off by adding the three properties, that is, displayNameDelegate, firstName, and lastName. The displayNameDelegate property contains an instance of the delegate type. This instance will be responsible for displaying the full name when the values of the firstName and lastName properties change.

Within the definitions for the firstName and lastName properties, we define the property observers. The property observers are called each time the value of the properties are changed. Within these property observers is where we call the displayName() method of our delegate instance to display the full name.

Now, let's create a type that will conform to the DisplayNameDelegate protocol. We will name this type MyDisplayNameDelegate:

struct MyDisplayNameDelegate: DisplayNameDelegate { 
    func displayName(name: String)  { 
        print("Name: \(name)") 
    } 
}  

In this example, all we will do is print the name to the console. Now, let's see how we would use this delegate:

var displayDelegate = MyDisplayNameDelegate() 
var person = Person(displayNameDelegate: displayDelegate) 
person.firstName = "Jon"  
person.lastName = "Hoffman" 

In the preceding code, we begin by creating an instance of the MyDisplayNameDelegate type and then use that instance to create an instance of the Person type. Now, when we set the properties of the Person instance, the delegate is used to print the full name to the console.

While printing the name to the console may not seem that exciting, the real power of the delegation pattern comes when our application wants to change the behavior. Maybe we want to send the name to a web service or display it somewhere on the screen, or even ignore the change. To change this behavior, we simply need to create a new type that conforms to the DisplayNameDelegate protocol. We can then use this new type when we create an instance of the Person type.

Another advantage that we get from using the delegation pattern is loose coupling. In our example, we separated the logic part of our code from the view by using the delegate to display the name whenever the properties changed. Loose coupling promotes a separation of responsibility, where each type is responsible for very specific tasks; this makes it very easy to swap out these tasks when requirements change, because we all know that requirements change often.

So far in this chapter, we have looked at protocols from a coding point of view. Now, let's look at protocols from a design point of view.

Designing with protocols

With protocol-oriented programming, we should always begin our design with the protocols, but how should we design these protocols? In the object-oriented programming world, we have superclasses that contain all the base requirements for the subclasses. Protocol design is a little bit different.

In the protocol-oriented programming world, we use protocols instead of superclasses, and it is preferable to break the requirements into smaller, more specific protocols rather than having bigger monolithic protocols. In this section, we will look at how we can separate the requirements into smaller, very specific protocols and how to use protocol inheritance and composition. In Chapter 3, Extensions, we will take this a little further and show you how to add functionality to all types that conform to a protocol using protocol extensions.

For the example in this section, we will model something that I enjoy building: robots. There are many types of robots with lots of different sensors, so our model will need the ability to grow and handle all the different options. Since all robots have some form of movement, we will start off by creating a protocol that will define the requirements for this movement. We will name this protocol RobotMovement:

protocol RobotMovement { 
    func forward(speedPercent: Double)  
    func reverse(speedPercent: Double)  
    func left(speedPercent: Double)  
    func right(speedPercent: Double) 
    func stop() 
} 

In this protocol, we define the five methods that all conforming types must implement. These methods will move the robot in the forward, reverse, left, or right directions, as well as stop the robot. This protocol will meet our needs if we only want the robot to travel in two dimensions, but what if we had a flying robot? For this, we would need our robot to also go up and down. To do this, we can use protocol inheritance to create a protocol that adds the additional requirements for traveling in three dimensions:

protocol RobotMovementThreeDimensions: RobotMovement { 
    func up(speedPercent: Double) 
    func down(speedPercent: Double) 
} 

Notice that we use protocol inheritance when we create this protocol to inherit the requirements from the original RobotMovement protocol. This allows us to use polymorphism, as described in the Polymorphism with protocols section of this chapter. This allows us to use instances of types that conform to either of these protocols interchangeably by using the interface provided by the RobotMovement protocol. We can then determine whether the robot can travel in three dimensions by using the is keyword, as described in the Type casting with protocols section of this chapter, to see if the RobotMovement instance conforms to the RobotMovementThreeDimensions protocol or not. Now, we need to add some sensors to our design. We will start off by creating a Sensor protocol that all other sensor types will inherit from. This protocol will contain four requirements. The first two will be read-only properties that define the name and type for the sensor. We will need an initiator that lets us name the sensor and a method that will be used to poll the sensor:

protocol Sensor { 
    var sensorType: String {get} 
    var sensorName: String {get set} 
 
    init (sensorName: String) 
    func pollSensor() 
} 

The sensor type will be used to define the type of sensor and will contain a string, such as DHT22EnvironmentSensor. The sensor name will let us distinguish between multiple sensors and will contain a string, such as RearEnvironmentSensor. The pollSensor() method will be used to perform the default operation by the sensor. Generally, this method is used to read the sensor at regular intervals. Now, we will create requirements for some specific sensor types. The following example shows how to create the requirements for an environment sensor:

protocol EnvironmentSensor: Sensor {  
    func currentTemperature() -> Double  
    func currentHumidity() -> Double 
} 

This protocol inherits the requirements from the Sensor protocol and adds two additional requirements that are unique for environment sensors. The currentTemperature() method will return the last temperature reading from the sensor and the currentHumidity() method will return the last humidity reading from the sensor. The pollSensor() method from the Sensor protocol will be used to read the temperature and humidity at regular intervals. Finally, the pollSensor() method will probably run on a separate thread. Let's go ahead and create a couple more sensor types:

protocol RangeSensor: Sensor { 
    func setRangeNotification(rangeCentimeter: Double, 
rangeNotification: () -> Void) func currentRange() -> Double } protocol DisplaySensor: Sensor { func displayMessage(message: String) } protocol WirelessSensor: Sensor { func setMessageReceivedNotification(messageNotification:
(String) -> Void) func messageSend(message: String) }

You will notice that two of these protocols (RangeSensor and WirelessSensor) define methods that set notifications (setRangeNotification and setMessageReceivedNotifications). These methods accept closures in the method parameters and will be used within the pollSensor() method to notify robot code immediately if something has happened. With the RangeSensor types, the closure will be called if the robot is within a certain distance of an object, and with WirelessSensor types, the closure will be called if a message comes in. There are two advantages that we get from a protocol-oriented design like this one. The first is that each of the protocols only contain the requirements that are needed for their particular sensor type. The second is that we are able to use protocol composition to allow a single type to conform to multiple protocols. As an example, if we had a display sensor that has Wi-Fi built in, we would create a type that conforms to both the DisplaySensor and WirelessSensor protocols. There are many other sensor types; however, this will give us a good start for our robot. Now, let's create a protocol that will define the requirements for the robot types:

 
protocol Robot { 
    var name: String {get set} 
    var robotMovement: RobotMovement {get set} 
    var sensors: [Sensor] {get} 
 
    init (name: String, robotMovement: RobotMovement) 
    func addSensor(sensor: Sensor) 
    func pollSensors() 
} 

This protocol defines three properties, one initiator, and two methods that will need to be implemented by any type that conforms with this protocol. These requirements will give us the basic functionality that's needed for the robots. It may be a bit confusing thinking about all of these protocols, especially if we are used to having only a few superclass types. It usually helps to have a basic diagram of our protocols. The following diagram shows the protocols that we just defined with the protocol hierarchy:

This gives us a basic idea of how to design a protocol hierarchy. You will notice that each of the protocols define the specific requirements for each device type. In Chapter 6, Protocol-Oriented Programming, we will go into greater detail on how to model our requirements with protocols. In this section, we used protocols to define the requirements for the components of a robot. Now it's your turn take a moment and see if you can create a concrete implementation of the Robot protocol without creating any concrete implementations of the other protocols. The key to understanding protocols is understanding how to use them without the concrete types that conform to them. In the downloadable code for this book, we have a sample class named SixWheelRover that conforms to the Robot protocol that you can compare your implementation to. Now, let's see how Apple uses protocols in the Swift standard library.

Protocols in the Swift standard library

Apple uses protocols extensively in the Swift standard library. The best resource that we have to see the makeup of the standard library is http://swiftdoc.org. This site shows us the types, protocols, operators, and globals that make up the standard library. To see how Apple uses protocols, let's look at the Dictionary type. This is a very commonly used type, but also one that has a pretty simple protocol hierarchy. From the http://swiftdoc.org/ main page, click on the Dictionary type. Then, scroll about halfway down the page until you see the Inheritance section, which should look similar to the following screenshot:

This section lists the protocols that the Dictionary type conforms to. If we click on the View Protocol Hierarchy → link, we will see a graphical representation of the protocol hierarchy, which will look similar to this:

As we can see from the preceding diagram, the Dictionary type conforms to five different protocols. We can also see that the Collection protocol inherits requirements from the Sequence protocol.

From the http://swiftdoc.org/ main page, we can click on each of the protocols to see their requirements. From this site, we can see that Apple uses protocols extensively within the Swift standard library. We will be looking at this site as we go through this book to see how Apple uses the various technologies that we are discussing.

Summary

While protocols themselves may not seem very exciting, they are actually quite powerful. As we saw in this chapter, we are able to use them to create very specific requirements. We can then use protocol inheritance and protocol composition to create protocol hierarchies. We also saw how to implement delegation patterns with protocols.

We concluded this chapter by showing you how we can model a robot with sensors using protocols and how Apple uses protocols in the Swift standard library.

In Chapter 3, Extensions, we will see how we can use protocol extensions to add functionality to types that conform to a protocol, but before we do that, let's look at our type choices.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Leverage the power of Protocol-Oriented Programming in your applications
  • Leverage generics to create very flexible frameworks
  • Learn how to implement common design patterns in a protocol-oriented way

Description

Protocol-oriented programming is an incredibly powerful concept at the heart of Swift's design. Swift's standard library was developed using POP techniques, generics, and first-class value semantics; therefore, it is important for every Swift developer to understand these core concepts and take advantage of them. The fourth edition of this book is improved and updated to the latest version of the Swift programming language. This book will help you understand what protocol-oriented programming is all about and how it is different from other programming paradigms such as object-oriented programming. This book covers topics such as generics, Copy-On-Write, extensions, and of course protocols. It also demonstrates how to use protocol-oriented programming techniques via real-world use cases. By the end of this book, you will know how to use protocol-oriented programming techniques to build powerful and practical applications.

Who is this book for?

This book is intended for Swift developers who have, at minimum an introductory knowledge of the Swift programming language and would like to understand how they can use Protocol-Oriented Programming techniques in their applications.

What you will learn

  • Learn the differences between object-oriented programming and protocol-oriented programming
  • Understand why value types should be prioritized over reference types
  • Delve into protocols, protocol inheritance, protocol composition, and protocol extensions
  • Learn how to implement COW (Copy-On-Write) within your custom value types
  • Understand how memory management works in Swift and how to avoid common pitfalls
  • Design applications by starting with the protocol rather than the implementation
Estimated delivery fee Deliver to Russia

Economy delivery 10 - 13 business days

$6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Jun 28, 2019
Length: 224 pages
Edition : 4th
Language : English
ISBN-13 : 9781789349023
Vendor :
Apple
Category :
Languages :
Tools :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Product feature icon AI Assistant (beta) to help accelerate your learning
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to Russia

Economy delivery 10 - 13 business days

$6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Jun 28, 2019
Length: 224 pages
Edition : 4th
Language : English
ISBN-13 : 9781789349023
Vendor :
Apple
Category :
Languages :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 120.97
Hands-On Design Patterns with Swift
$48.99
Mastering Swift 5
$38.99
Swift Protocol-Oriented Programming
$32.99
Total $ 120.97 Stars icon
Banner background image

Table of Contents

10 Chapters
Starting with the Protocol Chevron down icon Chevron up icon
Our Type Choices Chevron down icon Chevron up icon
Extensions Chevron down icon Chevron up icon
Generics Chevron down icon Chevron up icon
Memory Management Chevron down icon Chevron up icon
Object-Oriented Programming Chevron down icon Chevron up icon
Protocol-Oriented Programming Chevron down icon Chevron up icon
Adopting Design Patterns in Swift Chevron down icon Chevron up icon
Case Studies Chevron down icon Chevron up icon
Other Books You May Enjoy Chevron down icon Chevron up icon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact [email protected] with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at [email protected] using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on [email protected] with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on [email protected] within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on [email protected] who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on [email protected] within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela