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 the chapter will build on this understanding.
Protocol syntax
Defining a protocol
The syntax we use to define a protocol is very similar to the syntax used to define a class, structure, or enumeration. The following example shows the syntax 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 the 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 does not 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 the 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 next 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}
Now let's see 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 do not 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 is not required to be implemented. To use optional requirements, we need to start off by marking the protocol with the @objc attribute.
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() }
Now let's explore how protocol inheritance works.