5. Generics and Protocols

Swift is a type-safe language. You cannot pass strings or colors or floats or double values to a function expecting Int. If you try to do so, the compiler errors. Type safety constrains parameters to those types declared as legal in the signature. For a more general implementation, look to Swift generics.

Generics help you build robust code that expands functionality beyond single types. A generic implementation services an entire group of types instead of just one specific version. This approach enables you to minimize code duplication while serving types that share common behaviors and characteristics.

The combination of generic types and behavior contracts (called protocols) establishes a powerful and flexible programming synergy. This chapter introduces these concepts and explores how you can navigate this often-confusing development arena.

Expanding to Generics

Consider the following function. It compares two Int instances and returns a truth value that expresses whether two integer parameters are equal. You cannot call testEquality using doubles, Booleans, or strings. This super-basic implementation is specific to integers:

func testEquality(x: Int, _ y: Int) -> Bool {
    return x == y
}

Now, contrast that functionality with the following version. This function updates testEquality to accept a pair of generic parameters. Unlike the previous function, this version works with many types:

func testEquality<T where T:Equatable>(x: T, _ y: T) -> Bool {
    return x == y
}

In fact, this new version of testEquality works with any type that declares Equatable support. This updated version can compare strings, integers, doubles, Booleans, closed ranges, and so forth, all of which ship in standard Swift with that support built in, as well as any type for which you add that conformance. Since adding conformance is simply a matter of declaring protocol membership and implementing a single function, you can expand any type to be Equatable with a minimum of work. Once you do so, that type automatically works with testEquality.

The updated version of testEquality above uses a generic type declaration, which you see between angle brackets after the function name. Of course, both x and y must match each other’s type—the same T type is used to declare both value parameters—but the function now offers a much wider set of possible use domains. The function uses this declaration to type its parameters to arbitrary types instead of being limited to Int. In this example, the type constraint’s where clause mentions the Equatable protocol. This constraint limits types to items that can be compared for equality using ==. Other than these edits, the function is identical to the original version.

Protocols

A Swift protocol is a contract that mandates how items behave and communicate. It provides a blueprint that sets out a list of requirements for a particular role or task. When a class, a structure, or an enumeration adopts a protocol, it agrees to implement all the requirements laid out in that protocol. Implementing these features is called conforming to the protocol. In the case of Equatable, the protocol specifies that a conforming type implements a == equality function. Here are some key ways protocols work:

Image Protocols act as gatekeepers. When working with generics, protocols describe the minimum set of APIs required to power your functions. A protocol establishes compile-time checks which guarantee that all conforming types implement a certain base set of functionality. The testEquality example needs things that can be equated to each other. It doesn’t care what those things are or how they’re compared. It only matters that these items respond to a common function (==) and return a well-defined type, in this case Boolean.

Image Protocols act as mediators. They enable you to solve the problem of both “I want to apply this same functionality to different kinds of things,” as with testEquality and Equatable, and “I want to implement this same API interface in many different ways,” which you might see in an OutputDestination or IntegerConsumer protocol. These latter two protocols are entirely hypothetical. They demonstrate how you can use a protocol to delegate required functionality without mandating any specific implementation details.

Image Protocols act as traits. You can conform a type to multiple protocols and automatically receive any default implementations that have been established in protocol extensions. This building-by-composition approach introduces flexibility and power that you cannot gain from simple class inheritance. It limits duplicate code because it groups implementations by shared behaviors, not by object hierarchies. It flattens inheritance trees because you can build constructs using just the features you need for a given task.

Protocol Inheritance

Protocols offer multiple inheritance. That is, a protocol can inherit from one or more other protocols and add further requirements to that set of inherited requirements. In the following example, MyCustomProtocol inherits from Equatable and BooleanType:

protocol MyCustomProtocol: Equatable, BooleanType {
    // .. other requirements here ..
}

A conforming type, such as MyCustomStruct in the following example, must satisfy all the direct and inherited requirements of the protocols it adopts. In this example, Equatable is one of the inherited protocols, so any type that conforms to MyCustomProtocol must satisfy the requirements of Equatable:

struct MyCustomStruct: MyCustomProtocol { // declare conformance
    // implementation must satisfy Equatable, BooleanType,
    // plus any additional requirements introduced for MyCustomProtocol
}

MyCustomStruct conforms to MyCustomProtocol, and MyCustomProtocol inherits from Equatable. This means you can pass instances to any parameter that must conform to Equatable, so MyCustomStruct is ready to work with testEquality:

testEquality(MyCustomStruct(), MyCustomStruct()) // works

Protocols and Implementation

A protocol declaration doesn’t include implementation. It just lists requirements that specify what implementation details you must include. The compiler will not let you add bodies to protocol declarations. You can add defaults for type alias declarations, but that’s about it. Implementation is left up to conforming types (as with Equatable’s == function) or to protocol extensions that add default protocol conformance implementations. Equatable’s != functionality is implemented by the standard library and inherited for free by conforming types.

Tokens

The type used to describe both arguments in the updated testEquality function is T. There’s nothing magical about the letter T. This descriptive token provides context about how a stand-in type is used. You might use any other letter or string, such as Item or Element or Stream, for example. Reserve T for the most generic of generic types. Arrays use Element, and dictionaries use Key and Value type stand-ins because those names make more sense. Tokens help distinguish the role each type will play:

Array<Element>
Dictionary<Key: Hashable, Value>

Using a single letter when there’s no further semantic information about the type usage is conventional, or “what everyone else generally does.” Additional type stand-ins within a single implementation are normally U, V, and so forth. I’ve seen people use R for return types, G for generator types, I for index types, and S for sequence types (also S1, S2, and so on for multiple sequences). There are no hard-and-fast rules beyond keeping your tokens unique and unambiguous in the context in which they’re used.

That said, always prefer better semantics through words to single letters when possible. Element is superior to E, and Sequence is superior to S. I try to reserve single letters to T, U, and occasionally V—and only when truly working with the most generic types or when long names reduce the clarity of the declaration.

Type Constraints

The where clause between angle brackets is called a type constraint. It defines requirements for a generic type parameter. When you’re building super-simple conditions like the Equatable conformance used in the following function, Swift offers a shorthand for the where clause. Drop the “T where T” phrase and simply declare the protocol after a colon, as you see here:

func testEquality<T: Equatable>(x: T, _ y: T) -> Bool {
    return x == y
}

The result is simpler, and you don’t lose any clarity of intent. The Swift compiler knows how to parse this shorter form.

Type constraints can be as simple as a single protocol conformance, or they can establish complex relationships, as in the following example:

public init<C: CollectionType where C.Index: ForwardIndexType,
    C.Generator.Element == Element>(_ base: C)

This constraint allows a type to be initialized with collections that use an incrementable index. It also establishes a same-type constraint, specifying that the collection elements will match an element type used by this construct. Protocols add flexibility and power to development, but they, along with generics, can be intimidating, especially to developers new to the paradigm. Learning to create new type constraints and to understand constraints declared by the modules you use can be frustrating. I encourage you to persevere; you will be rewarded if you master these language features.

Adopting Protocols

The following snippet shows the standard library definition of the Equatable protocol. A protocol being declared does not directly implement the methods, properties, and other members it describes:

protocol Equatable {
    func ==(lhs: Self, rhs: Self) -> Bool
}

This declaration only says that to conform to Equatable, a type must implement the == operator. Protocols specify which elements must be included for classes, structures, and enumerations that adopt the protocol. Actual implementation details are left either to adopting types or to default implementations supplied by protocol extensions.

For example, here’s a custom type. It’s a new struct called Point that stores a pair of Double variables, an x and a y:

struct Point {
    var x: Double
    var y: Double
}

You create new instances by passing values for each member of the structure, as in the following examples:

var p1 = Point(x: 10, y: 30)
var p2 = Point(x: 10, y: 20)
var p3 = Point(x: 10, y: 20)

Although Point instances are clearly comparable in their design—they’re just a pair of floating-point numbers after all—you cannot pass these variables to the testEquality function, even in its generic form. Point does not adopt Equatable. The constraining where clause remains unsatisfied. To remedy this, declare Equatable and implement the == operator, as in the following snippet:

extension Point: Equatable {}
func ==(lhs:Point, rhs:Point)->Bool {return lhs.x==rhs.x && lhs.y==rhs.y}

You declare the == function outside the struct declaration. That’s because == is an operator, and Swift requires operators to be implemented at the global scope. After performing these changes, the generic testEquality function starts working with Point and can now be applied to instances created from this structure.

Declaring Protocols

Every protocol has a name that describes the type-specific qualities the protocol establishes. When establishing protocols, choose capitalized names that describe the role conforming types will fulfill. For example, the Equatable protocol mandates types that can be equated. The Comparable protocol that builds on Equatable adds ordering nuance to equivalence. Comparable types can describe binary relations like “greater than or equal to” and “strictly greater than” between ordered elements.

In a similar manner, convertible protocols like CustomStringConvertible, FloatLiteralConvertible, and DictionaryLiteralConvertible describe instances that can be converted and the manner in which they’ll be used. For example, FloatLiteralConvertible can be used to create an instance from a floating-point literal:

/// Conforming types can be initialized with floating point literals.
protocol FloatLiteralConvertible {
    typealias FloatLiteralType

    /// Create an instance initialized to `value`.
    init(floatLiteral value: Self.FloatLiteralType)
}

Like FloatLiteralConvertible, most other protocol declarations are quite short. Their goal is to describe a crucial set of requirements so you can build behaviors that depend on those features.

You declare a protocol using the protocol keyword. A protocol can establish a new conformance set (for example, Equatable) or can build off inherited characteristics (for example, Comparable, which inherits requirements from Equatable):

protocol protocol-name: inherited-protocols {
    // protocol-member-declarations
}

The simplest possible protocol looks like this:

protocol SimplestProtocol {} // requires nothing

This is not very useful. You might theoretically use something like SimplestProtocol to mark types that you would associate together for some task. Adding a no-requirements protocol ensures that you don’t have to implement any details, but that types can be tested by the compiler and at runtime for conformance.

What you don’t generally do is this:

protocol DerivedProtocol:  AncestorProtocol {}

I cannot think of meaningful circumstances in which you’d create a derived protocol without adding at least one new restriction.

Contrast that, however, with this next example, which creates a new protocol that blends the features of multiple parents but requires only a single point of conformance:

protocol UnionProtocol:  AncestorProtocol1, AncestorProtocol2 {}

To be fair, this isn’t a great choice to use, either. In its current form, it adds no new meaningful semantics and may hide details from clients; that is never a positive contribution. Protocol composition enables you to join protocols together into a single requirement. You add these between angle brackets after the protocol keyword, and you can list as many protocols as needed:

func doSomething(item: protocol<AncestorProtocol1, AncestorProtocol2>) {...}

A protocol composition does not define a new protocol type. It creates a temporary local protocol that combines the requirements of all the listed items.

Member Declarations

Protocol member declarations include property, method, operator, initializer, subscript, and associated type requirements. These components add state, behavior, initializer, lookup, and typing features to a protocol contract, describing the features a conforming construct must implement. The following protocol shows examples of what these members can look like:

protocol ComplexProtocol {

    // Establish associated types
    typealias Element // without constraint
    typealias Index: ForwardIndexType // type conformance

    // Readable and readwritable properties
    var readableProperty: Element {get}
    var readwritableProperty: Element {get set}
    static var staticProperty: Index {get}

    // Methods
    func method(foo: Self) -> Index
    static func staticMethod() -> Element

    // Initializers
    init(element: Element)
    init?(index: Index)

    // Subscripts
    subscript(index: Index) -> Element {get set}
}

Here are a few quick rules:

Image Place typealias declarations at the top of your protocols to create associated types. These are used both by the protocol members that follow and as type stand-ins when you’re creating generics. Moving them to the start of the protocol declaration ensures that they’re easy to reference when you need to look up terms.

Image Property requirements must be of variable type; that is prefixed with var and not let. Property implementations may substitute let for read-only get types, as in the following protocol and class:

protocol HasAnIntConstant {var myInt: Int {get}}
class IntSupplier: HasAnIntConstant {let myInt = 2}

Image Protocols use the static keyword for property and method requirements. Implementations use class for classes and static for structures, although starting in Swift 1.2, classes also accept the static keyword.

Do not confuse static/class annotations with the class keyword that can be used to mark a protocol as class-only, as in the following declaration. Marking a protocol with class enables you to use weak properties typed as the protocol:

protocol MyClassOnlyProtocol: class, PossibleInheritedProtocol {}

Image In protocols, the mutating keyword before func specifies a method that modifies its instance. Conforming classes don’t need to use this keyword in implementations, but structs and enumerations do, as in the following examples:

protocol Mutable{mutating func mutate()}
class MutatingClass: Mutable {
    var x = 0
    func mutate(){self.x = 999}
}
struct MutatingStruct: Mutable {
    var x: Int
    mutating func mutate(){self.x = 999}
}
enum MutatingEnumeration: Mutable {
    case MyCase(Int)
    mutating func mutate() {self = MyCase(999)}
}

Mutating requirements can be satisfied by non-mutating implementations. (If the method actually mutates the struct or enum instance, the mutating keyword remains, of course, non-optional.) You cannot satisfy a non-mutating member requirement with a mutating implementation.

Image When you add initializer members to a protocol, you must add the required keyword to your implementations except when the class is marked final:

protocol InitializableWithString {init(string: String)}
class StringInitializableClass1: InitializableWithString {
    required init(string: String ){} // required required
}
final class StringInitializableClass2: InitializableWithString {
    init(string: String ){} // required not required
}

Image A protocol’s failable initializer, such as init?(), can be satisfied with either a failable or a nonfailable implementation.

Image A nonfailable initializer, such as init(), is satisfied with either a nonfailable initializer (preferred) or an implicitly unwrapped failable initializer. The latter approach is dangerous, for self-evident reasons.

Image A Self requirement (that is, Self-with-an-uppercase-S) refers to the type that adopts a protocol. Self is a type placeholder for the conforming type. If that conforming type is Double, for example, then treat any references to Self in the protocol as Double.

Building a Basic Protocol

The FloatLiteralConvertible protocol mentioned earlier in this chapter describes types that can be initialized with floating-point instances. It’s used to mark types that convert from floating-point literals. The following snippet turns that notion on its head by introducing a protocol for types that can be converted to doubles. It mandates a single doubleValue property, which expresses a conforming instance’s value as a Swift Double:

// Declare DoubleRepresentable Protocol
public protocol DoubleRepresentable {
    var doubleValue: Double {get}
}

Here are conforming implementations for Double, Int, CGFloat, and Float:

// Requires Cocoa or UIKit for CGFloat support
extension Double: DoubleRepresentable {
    public var doubleValue: Double {return self}}
extension Int: DoubleRepresentable {
    public var doubleValue: Double {return Double(self)}}
extension CGFloat: DoubleRepresentable {
    public var doubleValue: Double {return Double(self)}}
extension Float: DoubleRepresentable {
    public var doubleValue: Double {return Double(self)}}

As you see, these implementations are identical except for the one for Double. This code is redundant but unavoidable. At this time, you cannot create a protocol requirement on another class. You can only state which properties, methods, and so on a conforming class itself implements.

If you could do otherwise, you might create a custom DoubleInitializable protocol with a requirement for Double.init(Self) and build a simple default implementation for doubleValue. By declaring DoubleInitializable, the Int, CGFloat, and Float types (along with any other types you might want to use) could declare conformance and inherit that identical implementation. This is not a reality and one I do not honestly anticipate ever being realized in Swift.

Once added to a project using the code you just saw, the DoubleInitializable protocol enables you to convert conforming instances to doubles by accessing a doubleValue property. Here’s an example of how that would look for an integer-sourced example:

let intValue = 23 // 23
let d = intValue.doubleValue // 23.0
print(d.dynamicType) // Swift.Double
print(d) // 23.0

Adding Protocol Default Implementations

It may look redundant to include an implementation for Double that returns a double value property, but it’s critical here, as with other protocol work, to consider how the contract will be consumed by other protocols and by generics. Protocols define the shape of the interface between structures, ensuring that they can be properly used for plug-and-play implementation. What follows is a real-world example of how a simple conformance detail propagates down to great utility.

The ConvertibleNumberType protocol in this snippet describes types that can be converted to common type destinations. Any compliant type can express itself in terms of these destinations:

public protocol ConvertibleNumberType: DoubleRepresentable {
    var doubleValue: Double { get }
    var floatValue: Float { get }
    var intValue: Int { get }
    var CGFloatValue: CGFloat { get }
}

I originally built this protocol to assist with Core Graphics development in Swift. Moving back and forth between Double, the Swift default, and CGFloat has proved to be a regular hassle. I decided that properties made my code more readable than endless back-and-forth casts. (Your opinion may, of course, vary from this viewpoint.) I implemented these type conversions using a protocol extension. All I had to do was declare type conformance:

extension Double: ConvertibleNumberType{}
extension Float: ConvertibleNumberType{}
extension Int: ConvertibleNumberType{}
extension CGFloat: ConvertibleNumberType{}

This works because Swift protocol extensions enable you to provide default implementations for method and property requirements. The following implementations automatically accrue to conforming types:

public extension ConvertibleNumberType {
    public var floatValue: Float {get {return Float(doubleValue)}}
    public var intValue: Int {get {return lrint(doubleValue)}} // rounds
    public var CGFloatValue: CGFloat {get {return CGFloat(doubleValue)}}
}

Each of the preceding property getters is built on top of doubleValue, which explains why it was so important to create that silly “return self” implementation. Without Double’s DoubleRepresentable conformance, it couldn’t use this default implementation. With it, Double automatically offers a CGFloatValue property accessor, along with the intValue and floatValue created by the protocol extension.

Recipe 5-1 combines the DoubleRepresentable and ConvertibleNumberType protocols into a single implementation. It uses fewer moving parts than the examples shown so far in this section:

Image Recipe 5-1 drops explicit DoubleRepresentable declarations; these are included in ConvertibleNumberType.

Image Recipe 5-1 also drops the details of the ConvertibleNumberType members. The doubleValue requirement is covered in DoubleRepresentable. The other requirements are provided through the ConvertibleNumberType extension.

When a protocol offers its own implementations, you need not mention those properties or methods in the protocol declaration itself. You see this with Equatable. Its only publicly required method is ==. Swift’s standard library provides the != variant implementation.

Recipe 5-1 Building Protocols with Default Implementations


//: Numbers that can be represented as Double instances
public protocol DoubleRepresentable {
    var doubleValue: Double {get}
}

//: Numbers that convert to other types
public protocol ConvertibleNumberType: DoubleRepresentable {}
public extension ConvertibleNumberType {
    public var floatValue: Float {get {return Float(doubleValue)}}
    public var intValue: Int {get {return lrint(doubleValue)}}
    public var CGFloatValue: CGFloat {get {return CGFloat(doubleValue)}}
}

//: Double Representable Conformance
extension Double: ConvertibleNumberType {
    public var doubleValue: Double {return self}}
extension Int: ConvertibleNumberType {
    public var doubleValue: Double {return Double(self)}}
extension CGFloat: ConvertibleNumberType {
    public var doubleValue: Double {return Double(self)}}
extension Float: ConvertibleNumberType {
    public var doubleValue: Double {return Double(self)}}


Optional Protocol Requirements

In Objective-C, a protocol establishes a communication contract between a client that adopts that protocol and a producer that sends callbacks to the client, using the protocol’s declared methods. By default, all declared methods are required. Objective-C also enables you to specify optional requirements. Unlike standard requirements, these are implemented as needed. Establishing an optional communication contract requires producers to check whether a client implements a selector (respondsToSelector:) before calling it. Swift-native protocols don’t offer optional protocol requirements.

Objective-C interoperability means Swift supports optional protocol requirements on @objc protocols. When a protocol is annotated with @objc, you can prefix a requirement with an optional modifier. These @objc optional protocols are limited to classes and cannot be adopted by structures or enumerations. The optional modifier enables you to extend your protocols to use optional requirements even if your code won’t ever be used with Objective-C. It’s inelegant, but it’s how Swift is currently designed. Once you add the optional modifier, you can use conditional chaining to shortcut execution when instances do not implement optional methods.


Note

While Objective-C allows you to declare MyType<SomeProtocol> *instance, you cannot declare a variable bound by both a class and a protocol in Swift.


Swift-Native Optional Protocol Requirements

It remains unclear whether optional method implementations are generally desirable in a value-typed world. That said, you could create a better equivalent Swift-native system without using the @objc keyword or limiting optional requirements to classes. In pure Swift, declare a method in a protocol and then implement a default version that method in a protocol extension:

protocol MyProtocol {
   func myOptionalMethod()
}
extension MyProtocol {
    func myOptionalMethod() {}
}

When working with delegate protocols, your extension implementation may need to return a default value. This approach enables adopting constructs to override default implementations. If they do not, they inherit the default behavior with only minor overhead. When they do, there’s a well-grounded contract for communication with an adopting class.

Further, any construct that consumes this protocol needn’t care whether an adopting type overrides this method or not. It can call it without checking in either case and with full safety. That is, of course, unless you go out of your way to ensure that an override is absolutely necessary, as in the following example:

extension MyProtocol {
    func myOptionalMethod() {fatalError("Implement this method!")}
}

Building Generic Types

A generic type is a class, a structure, or an enumeration that’s built to manage, store, or process arbitrary types. Generics enable you to focus development efforts on shared behavior instead of specific type details. Rather than thinking about how a construct works with, for example, integers, strings, or coordinates, a generic implementation create semantics decoupled from the specific types your construct handles.

Swift’s Array, Set, and Dictionary are all generic types. Generics enable them to store many kinds of instances, including numbers, strings, structures, and more. Their standard-library declarations underscore their generic implementation. Angle brackets follow the name of each type. Within these brackets, you find a list of one or more generic parameters:

struct Array<Element>
struct Set<Element: Hashable>
struct Dictionary<Key: Hashable, Value>

The number of parameters reflects the way types are used in the parent construct. Arrays and sets store collections of a single type such as String or AnyObject. In contrast, dictionaries span two types, one for keys and one for values. These types are unrelated in the generic parameter declaration. When used later for instances, they may be identical, as in [Int: Int] dictionaries, or distinct, as in [String: AnyObject] ones.

Type Parameters

Type parameters provide simple type stand-ins. Just as method parameters provide actors for values, type parameters offer ways to represent types. The type used for the following storage property is unknown until the construct is actually used in code:

public struct MyStruct <T> { // T is a type parameter
    let storage: T
}

You establish that type association by creating instances. The type parameter can either be inferred, as in the first of the following examples, or specified explicitly, as in the second.

MyStruct(storage: 15) // T is Int
MyStruct<String>(storage: "Test") // T is String

The fully qualified type includes the same angle brackets as the generic definition. Finalized type instances replace the generic stand-ins. A [String: AnyObject] dictionary is shorthand for Dictionary<String, AnyObject>. (As a rule, I use the former for declarations and the latter for generic type constraints.)

Recipe 5-2 creates a Bunch type. This is a simple generic type that stores an instance and a running count. You push new “copies” into the Bunch. You can also pop items off as long as sufficient versions remain in the count. When the count falls to zero, pops fail and return nil because there are no more copies to return.

Recipe 5-2 Establishing a Generic Type


//: A Bunch stores n instances of an arbitrary type
public struct Bunch<Element> {
    private let item: Element
    var count = 1

    // New instances always have one copy
    init(item: Element) {self.item = item}

    // Add items to the bunch
    mutating func push() {count++}

    // Copy items from the bunch
    mutating func pop() -> Element? {
        guard count > 0 else {return nil}
        count -= 1
        return item
    }
}


Because it is generic, Recipe 5-2 enables you to store bunches of any type. The implementation uses a type parameter called Element. This parameter establishes a stand-in type for item, is referenced in the construct initializer, and is used with the pop method that returns instances. The generic Element type is not known until the code is used by a client.

You can decorate generic type parameters just as you would with any Swift-supplied types. The pop() method adds a question mark because it returns an Optional instance. Since a bunch cannot return instances when its count hits zero, optionals enable this implementation to gracefully handle the no-more-copies-available case without error handling. When a bunch’s supplies are replenished with pushes, clients can renew their requests and fetch further copies using pop().

Generic Requirements

Set elements and dictionary keys conform to the Hashable protocol. This protocol ensures that elements can be converted to a unique identifier, preventing duplicate entries. The protocol is declared as a requirement. It acts as a type constraint on the generic parameter:

struct Dictionary<Key: Hashable, Value>

Generics use two kinds of type constraints: conformance constraints, as in this example, and same-type requirements, which equate two types. The following sections introduce these constraint styles.

Conformance Requirements

The first generic requirement type is protocol conformance, also called a conformance requirement. This requirement specifies that a type conforms to a specific protocol. The set and dictionary Hashable restrictions use this constraint type.

In the most succinct form, a protocol requirement follows a generic parameter, as in the following example. A colon appears between the type parameter and the protocol name:

public struct Thing<Item: Hashable> {
    private let thing: Item
}

This declaration is actually shorthand for the following snippet, which uses a fully qualified where clause instead of a type:protocol shorthand:

public struct Thing<Item where Item: Hashable> {
    private let thing: Item
}

Swift enables you to add multiple where clauses for compound conformance, as in the following example:

public struct Thing<Item where Item: Hashable, Item: Comparable> {
    private let thing: Item
}

But protocol composition offers a cleaner approach despite the extra angle brackets:

public struct Thing<Item: protocol<Hashable, Comparable>> {
    private let thing: Item
}

Since you must now construct Thing with items that conform to these protocols, the first of the following two examples succeeds, and the second fails. Strings are hashable and comparable, but CGPoints do not successfully meet these requirements:

Thing(thing: "Hello")
// Thing(thing: CGPoint(x: 5, y:10)) // error

Recipe: Same-Type Requirements

The second kind of requirement is a same-type restriction. This requirement equates two types. As you see in the following example, the second type (in this case, Int) need not be a type parameter:

public class Notion<C: CollectionType where C.Generator.Element == Int> {
    var collectionOfInts: C
    init(collection: C) {self.collectionOfInts = collection}
}

This example builds a class that stores a collection of integers. Type constraints ensure that the generic type is first a collection type (a conformance requirement) and then that its elements are Int (a same-type requirement):

let notion = Notion(collection: [1, 2, 3]) // works
// let notion = Notion(collection: ["a", "b", "c"]) // doesn't work

You cannot create a same-type restriction that creates two identically types parameters, as in the following example, because it results in a compiler error:

enum Pointed<T, U where T == U>{case X(T), Y(U)} // fail

You can, however, use same-type requirements to relate two generic types, such as Element and C in Recipe 5-3. The generic function in this recipe uses equality to test whether a collection contains a member, which is restricted to the same type.

Recipe 5-3 Adding Same-Type Requirements


func customMember<
    Element: Equatable, C: CollectionType
    where C.Generator.Element == Element>(
    collection: C, element: Element) -> Bool {

    return collection.map({$0 == element}).contains(true)
}


The compiler uses typing restrictions to ensure that you call CustomMember with matching arguments. You cannot, for example, pass an integer array and a string. These types do not pass the same-type requirement:

let memberArray = [1, 2, 3, 4, 5, 6]
CustomMember(memberArray, element: 2) // true
CustomMember(memberArray, element: 17) // false
// CustomMember(memberArray, element: "hello") // compiler error

Recipe 5-4 offers another take on type constraints. This function accepts a sequence of Hashable items and produces an array of unique elements. The Hashable constraint enables a Swift Set<T> to filter out unique elements. Since sets work only with hashable items, the same-type constraint ensures that the input sequence is constructed of those set-friendly elements.

Recipe 5-4 Using Type Constraints to Build Same-Type Results


// Normally it's better to use full words, like "unique" but this function
// calls back to historic uniq functions from other languages

func uniq<S: SequenceType, T: Hashable where
    S.Generator.Element == T>(source: S) -> [T] {
    // order not preserved
    return Array(Set(source))
}


The recipe’s T type parameter token is referenced in the function’s [T] array output and the uniqueItems declaration. You could replace these tokens at either or both points with the equivalent S.Generator.Element, as in the following example:

func uniq<S: SequenceType where S.Generator.Element:Hashable>
    (source: S) -> [S.Generator.Element] {
    return Array(Set(source))
}

Generic Beautification

Regardless of whether you use T or S.Generator.Element, the uniq function suffers from angle bracket overload. Too much constraint material is stuffed into and overflowing from this small function’s type restrictions. By converting complex functions to protocol extensions, you introduce what Apple calls generic beautification.

Several of uniq’s restrictions serve to mention that this function is specific to sequence types. Expressing this behavior within a protocol extension using a method instead of a function creates a better and simpler implementation:

extension SequenceType where Generator.Element:Hashable {
    func uniq() -> [Generator.Element] {
        return Array(Set(self))
    }
}

The extension uses a where clause to establish that the uniq method applies solely to Hashable collections. This restriction allows uniq to build sets from sequence elements. The result is more readable and maintainable.

Collections don’t really need another member function, in reality, but if they did, the same beautification process—constraining a protocol extension and converting to a method—could update Recipe 5-3 as well:

extension CollectionType where Generator.Element:Equatable {
    func customMember(element: Generator.Element) -> Bool {
        return self.map({$0 == element}).contains(true)
    }
}

Legal Tokens

You cannot refer to or mention Recipe 5-3’s Generator and Element tokens until you first establish that C is a CollectionType. These tokens are valid only in the context of a collection type generic. Setting that conformance opens the door to the same-type restriction for integers. The tokens you use in type parameter declarations and within closure bodies represent a mix of items you declare and items you inherit.

Type Parameters

You list the custom generic types in angle brackets to the right of the type or function you’re creating. These represent a base collection of custom tokens:

class Detail<A, B, C, D, E> {}

Protocols

Generic declarations can mention any protocol as a conformance requirement, whether custom or system supplied:

class Detail<A:ArrayLiteralConvertible, B:BooleanType> {}

Associated Types

A protocol’s typealias declarations build associated types. These types provide placeholder names (also known as aliases) used in type declarations and constraints. The following example declares a protocol with a CodeUnit alias and implements a conforming class that establishes a value for that type, in this example UInt32:

protocol SampleProtocol{
    typealias CodeUnit
}

class Detail: SampleProtocol {
    typealias CodeUnit = UInt32
    var idx: CodeUnit = 0
}

Matching Aliases

The two following generic functions reference the CodeUnit associated type in their type constraints:

func sampleFunc1<T:SampleProtocol where T.CodeUnit == UInt16>(x: T) {}
func sampleFunc2<T:SampleProtocol where T.CodeUnit == UInt32>(x: T) {}
// sampleFunc1(Detail()) // does not compile
sampleFunc2(Detail()) // compiles

Detail sets its CodeUnit type alias to UInt32. Because of this, only the second of the two function calls compiles. The first fails because Detail’s CodeUnit alias does not match SampleFunc1’s same-type constraint.

Protocol Alias Defaults

A protocol may constrain associated types by specifying that they conform to a protocol or be members of a given class:

protocol-associated-type-declaration →
    typealias-headtype-inheritance-clause typealias-assignment

Here’s a quick example that shows some of this in action. The following sample builds a protocol that defines an associated type with a default assignment:

// Protocol defines a default type, which is an array of
// the conforming type
protocol ArrayOfSelfProtocol {
    typealias ArrayOfSelf: SequenceType = Array<Self>
}

The Self keyword used in this example refers to the type that adopts this protocol. For integers, that type is Int, and ArrayOfSelf is [Int]. Once the protocol is declared, you can conform types to the protocol and then reference arrays of that type. The following snippet tests an array against Int.ArrayOfSelf, and it equates to true, using the default assignment:

extension Int: ArrayOfSelfProtocol {}
[1, 2, 3] is Int.ArrayOfSelf // true

You can also use the default with functions:

// This generic function references the type alias default
func ConvertToArray<T: ArrayOfSelfProtocol>(x: T) -> T.ArrayOfSelf {
    return [x] as! T.ArrayOfSelf // works
}

// Example of calling the function
let result = ConvertToArray(23)
print(result) // [23]
print(result.dynamicType) // Swift.Array<Swift.Int>

If you want to push the limit on this example, try changing the default in the protocol from Array<Self> so the forced cast to T.ArrayOfSelf throws a fatal error.

While this example is fairly contrived, the utility of default values is not. In the Swift standard library, CollectionType provides a default value for the Generator associated type with IndexingGenerator<Self>. This default enables all collections to automatically generate() themselves.

Collating Associated Types

Valid associated types are both listed directly within a protocol and inherited from other protocols. This means that tokens listed in the standard library in a protocol declaration may not be exhaustive. For example, the SequenceType protocol lists one typealias, Generator:

protocol SequenceType {
    /// A type that provides the *sequence*'s iteration interface and
    /// encapsulates its iteration state.
    typealias Generator: GeneratorType
    ...
}

Then GeneratorType itself lists an Element. It’s extremely common to constrain a generic sequence function on the type of its Generator.Element. You will not see these other associated types unless you get down and dirty in the standard library and explore them further through their references.

To collect types, you must ascend the conformance tree to find protocol ancestors. You should also search items mentioned in associated type declarations as these add further possible tokens that you may need in generic constraints.

I’ve posted source code for automating token collection at GitHub, at https://gist.github.com/erica/c50f8f213db1be3e6390. The output of this code should help you track down available vocabulary that you can use for building protocol-based type references.

Extending Generic Types

When extending a generic type, you can reference any and all type parameters declared in the initial type. You do not specify a new type parameter list or add new parameter names. For example, this Array extension references Element, which was defined in the original type definition:

extension Array {
    var random: Element? {
        return self.count > 0 ?
            self[Int(arc4random_uniform(UInt32(self.count)))] : nil
    }
}

You cannot add specific typing because doing so converts generic types into non-generic versions. The following example produces a compiler error:

extension Array where Element == Int {} // not allowed

You can, however, use protocols to narrow an extension’s range of application. Protocols limit extensions to conforming types. For example, this next snippet works only with arrays composed of Hashable elements:

extension Array where Element: Hashable {
    func toHashString() -> String {
        var accumulator = ""
        for item in self {accumulator += "(item.hashValue) "}
        return accumulator
    }
}

You can use this extension with arrays of Double but not CGPoint:

[1.0, 2.5, 6.2].toHashString() // works
// [CGPointMake(2, 3)].toHashString() // error

Using Protocols as Types

Protocols can often be used in place of Swift types, including as method parameters and return types and as the types of constants, variables, and properties. In the following snippet, the play() function accepts a single DeckType parameter:

protocol DeckType {
    func shuffle()
    func deal()
}

func play(deck: DeckType) {
    deck.shuffle()
    deck.deal()
}

No further typing information is provided in the function beyond the protocol. The compiler knows that protocol conformance is sufficient to ensure that the function code (first shuffling and then dealing) can be executed properly by the passed parameter.

Protocol-Based Collections

You can use protocol types to create collections. Protocol-based collections are heterogeneous by default as they limit membership based on protocol conformance and not type. For example, you might create an Array<DeckType> instance that would store any kind of object that adopted the DeckType protocol:

var heterogeneousArray = Array<DeckType>() // or [DeckType]()
heterogeneousArray.append(someConformingDeck)
heterogeneousArray.append(anotherDeckWithADifferentType)

To build a homogeneous DeckType array, replace DeckType with a specific type such as MyTypeThatConformsToDeckType. This is the default behavior for creating arrays, namely Array<Type> versus Array<Protocol>.

Self Requirements

In protocol declarations, Self requirements are placeholders for the specific type that conforms to that protocol. These enable you to differentiate between homogeneous (same type) and heterogeneous (same protocol but possibly different type) elements. Consider the following two alternative protocol members:

func matchesDeckOrder(deck: DeckType) -> Bool
func matchesDeckOrder(deck: Self) -> Bool

The first uses a DeckType parameter. This function can compare a deck against any other type that conforms to the protocol. The second uses Self. The comparison in this case is limited to decks of the same type, which is probably a much easier, consistent, and efficient function to implement but isn’t always what you want or need.

When you must compare heterogeneous elements, protocol extensions enable you to use uppercase-Self to restrict implementation and compare types. The following protocol extension implements a heterogeneous version of matchesDeck. This implementation is restricted to cases where Self is a collection and will only try to match if the other deck is of the same type:

extension DeckType where Self: CollectionType,
    Self.Generator.Element: Equatable {

    func matchesDeck(deck: DeckType) -> Bool {
        if let deck = deck as? Self { // test for same type
            if self.count != deck.count {return false} // same count
            return !zip(self, deck).contains({$0 != $1}) // same items
        }
        return false
    }
}

You can see this behavior in action by extending Array to conform to DeckType:

extension Array: DeckType {...}

The Self-powered default implementation enables you to compare decks by checking types, member count, and member equality even when the decks are of different types:

// Build some decks
let x = [1, 2, 3, 4] // reference deck
let y = [1, 2, 3, 4] // matches
let z = [1, 2, 3, 5] // doesn't
let w = ["a", "b", "c", "d"] // different type

x.matchesDeck(y) // true
x.matchesDeck(z) // false
x.matchesDeck(w) // false

Protocol Objects and Self Requirements

A protocol object refers to an object typed by a protocol. For example, this snippet creates an array of Basic-typed objects:

protocol Basic {}
var basicArray = Array<Basic>()

You can add an integer to this array by extending Int to conform to Basic:

extension Int: Basic {}
basicArray.append(5)

However, you cannot use protocol objects with any protocol that enforces Self or associated type requirements. For example, consider the following snippet:

protocol TypeAliasRequired {
    typealias Foo
}
// var typeAliasRequiredArray = Array<TypeAliasRequired>() // fail
// error: protocol 'TypeAliasRequired' can only be used as a generic
// constraint because it has Self or associated type requirements

protocol SelfRequired {
    func something(x: Self)
}
// var selfRequiredArray = Array<SelfRequired>() // fail
// error: protocol 'SelfRequired' can only be used as a generic
// constraint because it has Self or associated type requirements

The problem encountered is this: When you ask Swift to create a protocol object, you’re saying “build a heterogeneous collection of items, all of which conform to this protocol.” At the same time, the Self requirement says “members have to be able to refer to this specific type.” That works with a homogenous collection (all the same type) but not a heterogeneous one (all the same protocol but potentially different types). The same issue arises with any other type restriction, so you see it also with associated type requirements. You most commonly run into this issue when working with common protocols like Hashable and Equatable, both of which enforce Self requirements.

To fix this, either avoid Self and associated type requirements entirely or build generic constraint implementations that enforce homogeneity. The following example creates a homogenous typed array, whose members all belong to a single type that in turn conforms to Hashable:

// func f(array: [Hashable]) {} // fail, has Self requirements
func f<T:Hashable>(array: [T]) {} // works

Leveraging Protocols

Here are a few points to keep in mind when considering protocols:

Image Prefer functionality to implementation. Protocols tell you what a construct does and not how it does it. To create generics-friendly projects, focus your code on the connections between data creation and data consumption instead of the particulars of specific types.

Image Watch out for repeated code across different types. Redundant code segments with just a few type-specific changes hint at patterns that lend themselves to generic and protocol implementations. Focus on the commonalities of design to find your targets of opportunity.

Image One protocol does not rule them all. Keep your protocols short, sweet, and meaningful. Each one should be a noun (often ending with Type) or an adjective (ending with ible or able; see http://ericasadun.com/2015/08/21/swift-protocol-names-a-vital-lesson-in-able-vs-ible-swiftlang/ for details). Think of protocols as defining either “this is a specific kind of thing” or “this is able to do this kind of work.” Avoid overloading protocols with too many semantics that distract from a protocol’s one true calling.

Image Refactor functions with extensive angle bracket clauses to use protocol extensions and methods. Swift’s protocol extensions use where clauses to constrain where methods apply, enabling you to move clauses away from overloaded angle brackets and into a more meaningful context.

Image Conform at the highest possible level. When adding protocol conformance and default implementations, do so at the highest possible abstraction that still makes sense. Instead of adding separate adoptions for, for example, Int and String, check whether there is a unifying concept at a common protocol they both already adopt. If doing so becomes too general, create a new protocol, conform the types that apply, and maybe even add an extension to implement the generic behavior you’re looking for.

Image Design with collections in mind. Differentiate whether you will work with heterogeneous (same-protocol) or homogeneous (same-type) functions and design your protocols correspondingly. Self requirements enable you to add same-type constraints. Protocol names support common conformance.

Image It’s never too late to refactor. While it’s great to write generics and protocols right out of the box, it’s extremely common to develop code and then later consider how to retrofit.

Wrap-up

Protocols and generics are some of Swift’s most exciting features. Protocols define the shapes and behaviors of the types that adopt them, enabling you to create more generic implementations. Together, protocols and generics reduce code, raise the level of abstraction, introduce reuse, and generally make the world a better and happier place.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.201.204