December Code

Swift protocols interview questions, with answers

Swift is often described as a protocol-oriented language: much of the standard library is built from protocols such as Equatable, Sequence and Codable, with default behaviour supplied by extensions. Interviews use protocols to test design thinking as well as syntax, asking how protocols replace class inheritance and how generics use them.

The answers below cover protocols and their requirements, protocol extensions, extensions of existing types, associated types, synthesized conformances, protocol composition and generic constraints, with code compiled with swiftc 6.4. Then take the free Swift diagnostic — ten questions across every Swift topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.What is a protocol in Swift?

    In short: A protocol declares requirements, properties and methods, that structs, classes and enums can adopt and must then provide.

    A protocol defines a contract without an implementation: here, anything Named must have a readable name property. Structs, classes and enums adopt it by listing it after a colon and satisfying every requirement, and a stored let property satisfies a { get } requirement. Code can then work with any conforming type, as hello does through a generic parameter constrained to Named. Protocols can require initializers, static members and mutating methods, and a type can adopt any number of them, which is how Swift shares behaviour without class inheritance.

    protocol Named {
        var name: String { get }
    }
    struct City: Named {
        let name: String
    }
    func hello<T: Named>(_ x: T) {
        print("Hello, \(x.name)")
    }
    hello(City(name: "Goa"))
    // Hello, Goa
  2. 2.What are protocol extensions in Swift?

    In short: Extensions on a protocol add methods and computed properties to every conforming type, including default implementations of requirements.

    An extension of a protocol can implement methods using only what the protocol guarantees, and every conforming type then gets them for free: Tea below only provides price, yet it has withTax. When the extension implements one of the protocol's own requirements, that becomes a default implementation, which conforming types can use or replace with their own. This is how the standard library gives every Sequence methods such as map, filter and contains, and it is the core of protocol-oriented design: small protocols plus shared behaviour in extensions.

    protocol Priced {
        var price: Int { get }
    }
    extension Priced {
        func withTax() -> Int {
            price * 118 / 100
        }
    }
    struct Tea: Priced {
        let price = 50
    }
    print(Tea().withTax())
    // 59
  3. 3.What are extensions used for in Swift?

    In short: An extension adds methods, computed properties, initializers, nested types or protocol conformances to an existing type, even one you did not write.

    Extensions let you add functionality to any type, including Int, String and framework classes, without subclassing or access to the source. The code adds an isEven computed property to Int. Extensions can add methods, computed properties, convenience initializers, subscripts, nested types and protocol conformances, but not stored properties, and they cannot override existing methods. Teams also use extensions to organise code, putting each protocol conformance of a type in its own extension so related methods stay together.

    extension Int {
        var isEven: Bool {
            self % 2 == 0
        }
    }
    print(4.isEven, 7.isEven)
    // true false
  4. 4.What is an associated type in a Swift protocol?

    In short: A placeholder type named with associatedtype that each conforming type fills in, letting one protocol work with different element types.

    Some protocols need to talk about a type that depends on the conformer, such as the elements a container holds. associatedtype Item declares that placeholder, and each conforming type decides what Item is, often inferred from how it implements the requirements: Books returns [String], so its Item is String. Sequence and Collection are built this way, with Element as an associated type. Protocols with associated types are usually used as generic constraints or with some rather than as plain existential types.

    protocol Store {
        associatedtype Item
        func all() -> [Item]
    }
    struct Books: Store {
        func all() -> [String] {
            ["Swift", "Go"]
        }
    }
    print(Books().all().count)
    // 2
  5. 5.How does Swift synthesize Equatable and Hashable conformance?

    In short: Declaring conformance on a struct or enum whose stored properties are all Equatable or Hashable makes the compiler write == and hash(into:).

    For structs whose stored properties all conform, and for enums whose associated values all conform, adding : Equatable or : Hashable is enough: the compiler generates == comparing every property and hash(into:) combining them. That is why two Pt values with the same coordinates count as one element in a Set below. Codable is synthesized the same way from the properties. You write the methods yourself only when equality should ignore some properties or use a custom rule, such as comparing identifiers only.

    struct Pt: Hashable {
        var x: Int, y: Int
    }
    let a = Pt(x: 1, y: 2)
    let b = Pt(x: 1, y: 2)
    let s = Set([a, b])
    print(a == b, s.count)
    // true 1
  6. 6.What is protocol composition in Swift?

    In short: Writing A & B as a type requires a value that conforms to both protocols, without declaring a new protocol that combines them.

    A parameter sometimes needs several capabilities at once. Protocol composition, written with &, creates a temporary requirement that the value conforms to all the listed protocols, so show below accepts anything that is both Named and Aged. It avoids defining an extra protocol for every combination. A composition can also include one class, requiring a subclass of it that conforms to the protocols. Codable itself is a composition: it is a typealias for Decodable & Encodable.

    protocol Named {
        var name: String { get }
    }
    protocol Aged {
        var age: Int { get }
    }
    struct Kid: Named, Aged {
        let name = "Ria", age = 9
    }
    func show(_ p: Named & Aged) {
        print(p.name, p.age)
    }
    show(Kid())
    // Ria 9
  7. 7.How do generic constraints work in Swift?

    In short: A constraint such as <T: Equatable> limits a generic parameter to types that conform, so the function can use that protocol's operations.

    A generic function works with any type, but it can only use operations that the type is known to support. Constraining the parameter, as T: Equatable does below, allows == on its values, and the function then works with integers, strings or any other Equatable type. More complex rules go in a where clause, such as where T: Hashable, or requirements on a collection's elements. The compiler checks the constraints at each call site and specialises the code, so generics cost nothing at run time in optimised builds.

    func allSame<T: Equatable>(
        _ a: [T]
    ) -> Bool {
        a.allSatisfy { $0 == a[0] }
    }
    print(allSame([2, 2, 2]))
    print(allSame(["a", "b"]))
    // true
    // false

How the diagnostic asks it

One question from the Swift bank, exactly as a sitting would show it. The bank has 4 on protocols & extensions and 30 across Swift.

Protocols & Extensions · easySWIFT-012

What does this Swift code print?

protocol Greeter {
    func hi() -> String
}

extension Greeter {
    func hi() -> String { "hello" }
}

struct En: Greeter {}

struct Fr: Greeter {
    func hi() -> String { "bonjour" }
}

print(En().hi(), Fr().hi())
  1. 1hello bonjourcorrect
  2. 2hello hello
  3. 3It does not compile: En does not implement hi
  4. 4bonjour bonjour

The extension on Greeter provides a default implementation of the requirement hi, so En conforms without writing hi itself and gets "hello". Fr supplies its own hi, which replaces the default, so it returns "bonjour". The output is hello bonjour. hello hello assumes the extension always wins. The compile error would be right without the extension: a type must satisfy every requirement, and the default counts. bonjour bonjour assumes one type's implementation is shared. Defaults in protocol extensions are the core of protocol-oriented Swift.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 Swift questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated