Lesson 72Protocols

Protocol Requirements

Types of Requirements

Protocols can require different types of members. Understanding each type helps you design better protocols!

Requirement Types

Properties

Instance & static properties

var name: Type { get set }

Methods

Instance & static methods

func doWork()

Initializers

Required constructors

init(param: Type)

Subscripts

Index access

subscript(i: Int) -> T

Mutating Methods

Use mutating for methods that modify value types:

mutating func toggle()

Classes don't need the mutating keyword when implementing.

Class Initializer Rules

  • Use required keyword in class implementations
  • Failable init init? can be satisfied by init or init!
  • Non-failable init can satisfy init? requirement

Associated Types Preview

Protocols can have placeholder types using associatedtype. We'll cover this in detail in Lesson 74!

main.swift
// PROTOCOL REQUIREMENTS
// Different types of requirements protocols can define

// PROPERTY REQUIREMENTS
protocol FullyNamed {
    var fullName: String { get }  // Read-only requirement
}

protocol Configurable {
    var isEnabled: Bool { get set }  // Read-write requirement
}

struct Person: FullyNamed {
    var firstName: String
    var lastName: String

    var fullName: String {  // Computed property satisfies get
        return "\(firstName) \(lastName)"
    }
}

class Device: Configurable {
    var isEnabled: Bool = false  // Stored property satisfies get set
}

// STATIC PROPERTY REQUIREMENTS
protocol Countable {
    static var count: Int { get set }
}

struct Item: Countable {
    static var count: Int = 0

    init() {
        Item.count += 1
    }
}

// METHOD REQUIREMENTS
protocol Toggleable {
    mutating func toggle()
}

struct Switch: Toggleable {
    var isOn: Bool = false

    mutating func toggle() {
        isOn = !isOn
    }
}

// Note: Classes don't need 'mutating'
class Light: Toggleable {
    var isOn: Bool = false

    func toggle() {  // No mutating keyword needed
        isOn = !isOn
    }
}

// STATIC METHOD REQUIREMENTS
protocol Creatable {
    static func create() -> Self
}

class Document: Creatable {
    required init() { }

    static func create() -> Self {
        return self.init()
    }
}

// INITIALIZER REQUIREMENTS
protocol Initializable {
    init(value: Int)
}

// Classes must use 'required' keyword
class Counter: Initializable {
    var value: Int

    required init(value: Int) {
        self.value = value
    }
}

// Structs don't need 'required'
struct Score: Initializable {
    var value: Int

    init(value: Int) {
        self.value = value
    }
}

// FAILABLE INITIALIZER REQUIREMENTS
protocol StringInitializable {
    init?(from string: String)
}

struct Age: StringInitializable {
    var value: Int

    init?(from string: String) {
        guard let age = Int(string), age >= 0 else {
            return nil
        }
        self.value = age
    }
}

// SUBSCRIPT REQUIREMENTS
protocol Indexable {
    subscript(index: Int) -> String { get }
}

struct StringList: Indexable {
    var items: [String]

    subscript(index: Int) -> String {
        return items[index]
    }
}

// ASSOCIATED TYPE REQUIREMENTS (Preview)
protocol Container {
    associatedtype Item
    var items: [Item] { get set }
    mutating func append(_ item: Item)
}

struct IntContainer: Container {
    var items: [Int] = []

    mutating func append(_ item: Int) {
        items.append(item)
    }
}

Try It Yourself!

Create a Resettable protocol with a mutating reset() method and a static defaultValue property. Implement it in a Counter struct!