Compiled Chronicles

A software development blog by Angelo Villegas

Swift: Protocols

In Objective-C, you are allowed to define protocols which declare the methods expected to be used for a particular situation. In t, protocols are basically a named contract that your types should conform to. If your type says it conforms to Equatable, then it better fulfil all of the required methods to make it equatable.

Protocol

protocol defines the body of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol doesn’t actually provide an implementation for any of these requirements, it only describes what an implementation will look like. The protocol can then be adopted by a typestruct, or enum to provide an actual implementation of those requirements. Any type that declares itself to conform to this protocol must implement the requirements dictated in the protocol.

Protocols can require that conforming types have specific instance properties, instance methods, type methods, operators, and subscripts.

Syntax

You define protocols in a very similar way to types, structs, and enums:protocol SomeProtocol { // protocol definition goes here func someFunc() // sample method }

Implementation

The protocol name is appended beside the superclass name, separated with a comma. If there isn’t a superclass, then you just write the protocol in place of the superclass.// protocol without superclass class MyClass : SomeProtocol { // Conforming to SomeProtocol func someFunc() { } } // protocol with superclass class AnotherClass : SomeSuperClass, SomeProtocol { // A subclass conforming to SomeProtocol func someFunc() { } }

Declaring Requirements

A protocol can require any type that comply to provide an instance or type property with a particular name and type. The protocol doesn’t require whether the property should be a stored property or a computed property – it only specifies the required property name and type. The protocol also specifies whether each property must be gettable or gettable and settable.

If a protocol requires a property to be gettable and settable, that property requirement cannot be fulfilled by a constant stored property or a read-only computed property. If the protocol only requires a property to be gettable, the requirement can be satisfied by any kind of property, and it is valid for it to be settable if this is useful for your own code.

Property requirements are always declared as variable properties, prefixed with the var keyword. Gettable and settable properties are indicated by writing { get set } after their type declaration, and gettable properties are indicated by writing { get }.protocol SomeProtocol { var mustBeSettable: Int { get set } // read-write var doesNotNeedToBe: Int { get } // read-only }

The mustBeSettable property is { get set }, so it must be settable and thus must be a var in the type that conforms to this protocol. If you want a property like doesNotNeedToBe as immutable, you can not use let but you should rather use var and only declare a { get } specifier.

Protocols

As far as Swift is concerned, there are three protocols you can use to broaden your object’s capabilities: EquatableComparable, and Printable. This is referred to as Apple’s official Swift guide protocols. It is possible to guess the protocol’s function to induce a specific behaviour just by reading their name.

Equatable Protocol

Objects who comply with the Equatable protocol can be evaluated for equality and inequality, hence the name – Equal. Declaring an object as equatable provide several useful capabilities, notably the capability of making it possible to determine whether two values of the same type are considered equal.

For a type to be Equatable, an implementation of the == operator must exist.func ==(lhs: Self, rhs: Self) -> Bool

For value types, equality is determined by evaluating each property’s equality. The method returns a Bool value type, this means the method should return true if the provided values are equal, otherwise return false.

As an example, consider a struct with properties namevalue, and category with types StringInt, and String respectively.struct Person { var name: String var value: Int var category: String }

Since our struct is comprised of three properties, two Person are equal if, and only if, the properties’ respective values are equal.// MARK: – Extensions extension Person : Equatable {} func ==(lhs: Person, rhs: Person) -> Bool { return lhs.name == rhs.name && lhs.value == rhs.value && lhs.category == rhs.category }let value1 = Person(name: “Anna”, value: 6, category: “Ballet”) var value2 = Person(name: “Giesel”, value: 5, category: “Contemporary”) let firstCheck = value1 == value2 // firstCheck is false value2.name = “Anna” let secondCheck = value1 == value2 // secondCheck is false value2 = value1 let thirdCheck = value1 == value2 // thirdCheck = true let fourthCheck = value1 != value2 // fourthCheck is false

An implementation of `!=` is automatically added drawing it’s implementation from the standard library.

!= is implemented as a generic function for Equatable and any type that conforms to Equatable automatically gets the != operator.

If you really wanted to, you can override the implementation of != :func !=(lhs: Person, rhs: Person) -> Bool { return !(lhs == rhs) }

Operators: Equatable Protocol
OperatorDescription
==Determines whether the left hand value is equal with the right hand value
!=Determines whether the left hand value is not equal with the right hand value

It makes sense that two name property of the struct with same values would be equal, but two Person objects can have the same name, but be different people.

Comparable Protocol

The Comparable protocol makes it possible to compare two values of the same type.

Building on EquatableComparable allows for more specific inequality, distinguishing cases where the left hand value is greater than the right hand value.

There is one required operator overload defined in the protocol <, as well as one defined in the inherited Equatable protocol ==. By adopting the Comparable protocol and adding an operator overload for <, you automatically gain the ability to use ><=, and >=.func <(lhs: Self, rhs: Self) -> Bool func <=(lhs: Self, rhs: Self) -> Bool func >(lhs: Self, rhs: Self) -> Bool func >=(lhs: Self, rhs: Self) -> Bool

Operators: Comparable Protocol
OperatorDescription
<Determines whether the left hand value is less than the right hand value
<=Determines whether the left hand value is less than or equal with the right hand value
>Determines whether the left hand value is greater than the right hand value
>=Determines whether the left hand value is greater than or equal with the right hand value

Since the Comparable protocol inherits the Equatable protocol, == is used with < and > operators to create <= and >= operators.

Extending our Person type to comply with the Comparable protocol:extension Person : Comparable {} func <(lhs: Person, rhs: Person) -> Bool { return lhs.name < rhs.name && lhs.category < rhs.category }

In this example, we excluded the value property to make the comparing more clearer by using a single property type: Stringvalue2 = Person(name: “Giesel”, value: 5, category: “Contemporary”) // Let’s reassign the value let fifthCheck = value1 < value2 // fifthCheck is true let sixthCheck = value1 > value2 // sixthCheck is false

Just like the != operator of the Equatable protocol, all other Comparable operators are automatically added after declaring the < operator. It’s because of the Swift Standard Library provides a default implementation of the Comparable protocol based entirely on the existential type _Comparable.

This means that all other operators can be derived from == and <, all of that functionality is made available automatically through type inference.

Printable Protocol

Printable Protocol is the third and last protocol on Apple’s official Swift guide protocols.

Objective-C has a similar method `- description` that returns an `NSString`.

The Printable protocol allows you to customize the textual representation of any type ready for printing.

A type must adopt this protocol if you wish to supply a value of that type to the print(_:) and println(_:) functions.

Extending, again, our Person type to comply with the Printable protocol:extension Person : Printable { var description: String { return “<\(self): Name: \(self.name), Value: \(self.value), Category: \(self.category)>” } }

This protocol only consist of one variable: description; a string containing a suitable textual representation of the receiver and is read-only.print(value2.description) // <__lldb_expr_138.Person: Name: Giesel, Value: 5, Category: Contemporary>

This property is required for any type that adopts the Printable protocol. Use it to determine the textual representation to print when, for example, you need to display to the user their list of to dos or shopping list.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *