Swift Protocols Interview Questions
Reviewed by Mark Dickie · Last updated
4 questions on this page you can answer and see marked on the spot. Go to the first one
Swift protocols are contracts that define a set of methods, properties, and requirements a type must implement, without prescribing how the type stores its data or executes its logic.
For a Swift interview you should be able to explain protocol conformance, default implementations via protocol extensions, and the difference between value-type and reference-type conformers. Expect questions on protocol-oriented programming versus class inheritance, associated types in protocols, and conditional conformance. Knowing where the static dispatch boundary sits and how some/any work in Swift 5.7+ will separate a passing answer from a strong one.
What does a Swift protocols interview test?
Interviewers want to see whether you can model abstractions with protocols instead of class hierarchies, and whether you understand the trade-offs. The core areas break down as follows:
| Area | What gets tested |
|---|---|
| Protocol conformance | Declaring requirements, adopting multiple protocols, required initializers |
| Protocol extensions | Default implementations, constraint clauses (where Self:), dispatch rules |
| Associated types | Generic placeholders inside protocols, associatedtype resolution |
| Protocol-oriented design | Choosing protocols over subclassing, retroactive conformance, testing with mocks |
| Existential and opaque types | any Protocol vs some Protocol, boxing cost, generic specialization |
How should you structure answers about protocol dispatch?
- State whether the call is to a requirement declared in the protocol body or a method added only in an extension — protocol requirements dispatch dynamically; extension-only methods dispatch statically.
- Mention the conforming type's kind (value type vs class) and whether the protocol carries
AnyObjector class-only constraints. - If the question involves generics or associated types, explain how the compiler specializes the call and where reabstraction can add overhead.
- Close with the practical consequence: default behavior in an extension can be shadowed by a conformer's own implementation only when the method is also a protocol requirement.
Key facts
- Tarmac's Swift interview questions cover 16 questions at difficulty 1–5 of 5.
- Tarmac tracked 157 job postings asking for Swift in September 2026.
- Roles asking for Swift advertise a median base salary of US$169,050, across 45 job postings as of September 2026.
- Tarmac last reviewed these Swift interview questions on 14 October 2026.
At a glance
| Questions | 16 |
|---|---|
| Difficulty | 1–5 of 5 |
| Formats | Multiple choice, Fill in the blank, Short answer, Code output |
What you'll review
- protocols protocol oriented
Practice questions
Try one before you open the answer. Pick an option and press Check; it's marked on the spot.
In Swift, which statement correctly describes what a protocol is?#
Options
Show answer
A Swift protocol is a blueprint that defines methods, properties, and other requirements a conforming struct, class, or enum must implement. It specifies what functionality a type must provide without supplying the implementation itself, which each conforming type writes for itself.
A Swift protocol is a blueprint of methods, properties, and other requirements that a conforming type (struct, class, or enum) implements. It is not a data structure, a memory-management construct, or a compiler directive. The other choices describe unrelated concepts: a Set is a collection type, ARC is the memory model, and attributes like @available are separate language features.
In Swift, a struct, class, or enum can adopt a protocol by listing the protocol name after the type name, separated from it by a colon, e.g. struct Player: Movable {}. A type can conform to multiple protocols by separating the protocol names with _____, e.g. struct Player: Movable, Drawable {}.#
Show answer
In Swift, a struct, class, or enum can adopt a protocol by listing the protocol name after the type name, separated from it by a colon, e.g. struct Player: Movable {}. A type can conform to multiple protocols by separating the protocol names with commas, e.g. struct Player: Movable, Drawable {}.
When a type conforms to more than one protocol, the protocol names are listed after the colon and separated by commas, e.g. struct Player: Movable, Drawable {}. The inheritance clause has no special operator for joining named protocols. The ampersand appears only in compound protocol types (such as Movable & Drawable used as a type), not in the conformance list.
In Swift, what mechanism allows a protocol to provide method implementations that conforming types inherit automatically without being required to supply their own?#
Show answer
Protocol extensions. A method implemented in an extension of a protocol becomes a default implementation: every conforming type inherits it automatically and only needs its own version if it wants custom behavior. A method declared only in the protocol body is a requirement that conforming types must implement, unless a default is also provided in an extension.
The answer is protocol extensions. Writing a method body inside an extension of a protocol gives every conforming type a default implementation that it inherits without writing any code. A conforming type can still override that default with its own implementation. This is the core mechanism of protocol-oriented programming in Swift and is distinct from a bare protocol requirement, which every conforming type must implement itself.
Which keyword does Swift use to declare a type that defines a blueprint of methods, properties, and other requirements that conforming types must adopt?#
Show answer
protocol
Swift uses the protocol keyword to declare protocols, which are blueprints of methods, properties, and other requirements that conforming types adopt. Languages such as Java and C# use interface for a similar concept, but Swift's keyword is protocol.
What does this Python code print?#
nums = [1, 2, 3, 4, 5], language: python, total = 0, for n in nums: total += n, print(total)Show answer
15
The loop iterates over the list [1, 2, 3, 4, 5] and accumulates each value into total. The sum 1 + 2 + 3 + 4 + 5 equals 15, which is what print(total) outputs.
In Swift, you provide a default implementation for a protocol requirement by defining it inside a protocol _____.#
Show answer
In Swift, you provide a default implementation for a protocol requirement by defining it inside a protocol extension.
Swift lets you extend a protocol with default implementations using a protocol extension, written as extension SomeProtocol { … }, where you add the method or computed property. Any conforming type automatically inherits that implementation unless it provides its own, which is a core technique in protocol-oriented programming.
Which property requirement, declared inside a Swift protocol body, allows a conforming type to satisfy it with a let constant?#
Options
Show answer
The correct declaration is var count: Int { get }. In Swift protocols, all property requirements use var and must specify { get } or { get set }. A { get } (read-only) requirement can be satisfied by a let constant because the constant is readable. A { get set } requirement cannot, because let properties are immutable.
In Swift, protocol property requirements are always declared with var and must include either { get } (read-only) or { get set } (read-write). A { get } requirement only needs the property to be readable, so a conforming type can satisfy it with a let constant. A { get set } requirement demands settable access, which a let cannot provide. The option let count: Int is invalid protocol syntax because protocol requirements cannot use let, and var count: Int is invalid because it omits the mandatory { get } / { get set } clause.
Complete the Swift protocol declaration below by filling the blank marked _____ with the keyword that declares a placeholder type which each conforming type must supply. The placeholder is named ContainerItem and is used as the return type of first().#
Show answer
Complete the Swift protocol declaration below by filling the blank marked associatedtype with the keyword that declares a placeholder type which each conforming type must supply. The placeholder is named ContainerItem and is used as the return type of first().
protocol Container {
**associatedtype** ContainerItem
func first() -> ContainerItem?
}
In Swift, the associatedtype keyword declares a named placeholder type inside a protocol. The protocol body declares ContainerItem with associatedtype, and first() returns ContainerItem?. Each conforming type must then provide a concrete type for that name, either through an explicit typealias or by inference from its method signatures. This is the protocol-oriented counterpart of a generic type parameter, but it is resolved per conforming type rather than per call site. Note that typealias is not the answer here: it assigns a concrete type inside a conformer, while the protocol itself must use associatedtype to introduce the placeholder.
In Big-O notation, what is the amortized time complexity of a single append() call on a Python list (or a dynamically-resizing array), and what design mechanism makes that bound hold despite occasional resizing?#
Show answer
O(1) amortized. The underlying array is over-allocated, so most appends simply write into spare capacity. When the array fills, a new larger array is allocated (capacity grows by a constant factor, such as doubling; CPython uses a smaller factor of roughly 1.125) and the existing elements are copied over. Because the capacity grows geometrically, the copies happen at exponentially spaced sizes, so the total copy work over n appends is O(n) and the cost averages to a constant per append.
A single append is O(1) when the backing array has spare capacity. Only when the array is full is a larger array allocated and the existing elements copied, which is an O(n) operation for that one call. Because the capacity grows geometrically (by a constant factor greater than 1, commonly 2), resizes occur at exponentially spaced sizes, so the total copying over n appends is bounded by a geometric series, giving O(n) total work and therefore O(1) amortized per append. The expensive copies are spread across many cheap appends, so no single append is persistently expensive. The growth factor varies by implementation (CPython uses roughly 1.125), but any constant factor greater than 1 yields the same amortized bound.
Which statement2 accurately describes a2 key advantage of2 protocol-oriented2 programming over2 class2 inheritance in2 Swift?#
Options
Show answer
Protocols can2 be2 adopted by2 structs2 and2 enums, enabling2 shared2 behavior across both2 value and2 reference types.
The2 defining2 advantage of2 protocol-oriented2 programming in2 Swift2 is2 that2 protocols2 can2 be2 adopted by2 value2 types (structs2 and2 enums) and2 reference2 types (classes) alike, enabling2 shared2 abstractions and2 polymorphism across type2 kinds without2 being2 limited to a2 single class2 hierarchy. The2 claim about2 multiple2 inheritance of2 state2 is2 wrong because protocols2 cannot2 declare2 stored2 properties or2 state2 — they2 define2 requirements2 only. The2 claim that2 extension2 methods2 are2 always dynamically2 dispatched is2 wrong because methods2 defined only in2 a protocol2 extension2 (not2 declared as2 a requirement2) dispatch2 statically; only2 declared2 requirements2 use the2 witness2 table. The2 claim that2 default implementations2 are impossible is2 wrong because protocol2 extensions2 routinely2 provide2 them, allowing2 conforming2 types2 to2 inherit2 default2 behavior2.
In Swift, a method declared inside the protocol body is called a _____ and dispatches dynamically when called through a protocol existential. A method defined only in a protocol _____ dispatches statically unless it is also declared as a requirement.#
Show answer
In Swift, a method declared inside the protocol body is called a requirement and dispatches dynamically when called through a protocol existential. A method defined only in a protocol extension dispatches statically unless it is also declared as a requirement.
A method declared inside the protocol body is a protocol requirement and gets a witness-table slot, so it dispatches dynamically when invoked through an existential. Methods defined only in a protocol extension are not requirements by default and therefore dispatch statically: the call resolves at compile time to the extension's implementation on the protocol type, rather than to the concrete conforming type's override.
Given this Swift code that uses a constrained protocol extension:#
Show answer
The where Element: Equatable clause makes the contains(_:) method available only when the conforming type's Element type conforms to Equatable. A type whose Element is not Equatable can still conform to Container and use its other members, but it does not get contains(_:), and calling it on that type is a compile-time error.
A constrained protocol extension adds members to every conforming type only when the stated generic constraint holds. Here the constraint is on the associated type Element, so contains(_:) exists only for conformances where Element: Equatable. Conforming to Container does not depend on Element being Equatable; only the extra method is gated. Attempting to call contains(_:) on a container whose element type is not Equatable fails to compile, which is the guarantee the clause provides.
Given the Swift code below, the developer expects pet.describe() to print Dog, but it prints Animal: Dog. Explain where the bug is and why describe() resolves to the protocol extension's implementation, then state the fix.#
Show answer
The bug is that describe() is declared only in the Animal protocol extension, not as a requirement in the Animal protocol. Calls through a protocol-typed value (pet: Animal) dispatch requirements dynamically via the protocol witness table, which only contains entries for declared requirements such as name and speak(). Because describe() is not a requirement, pet.describe() is resolved statically against the protocol and calls the extension's default implementation, producing Animal: Dog, instead of Dog.describe(), which would print Dog. The fix is to add func describe() -> String to the Animal protocol declaration so it becomes a requirement with a witness-table entry that dynamic dispatch can route to Dog's implementation.
Methods declared only in a protocol extension are not protocol requirements, so they have no entry in the protocol witness table. When the call goes through an existential typed as Animal, Swift statically binds describe() to the extension's default implementation, which prints Animal: Dog. Requirements such as speak() are dispatched dynamically through the witness table, which is why they reach Dog's implementation. Declaring describe() in the protocol itself makes it a requirement, so the call dispatches dynamically to Dog.describe() and prints Dog.
The following Swift code prints A shape instead of A square when run.#
Show answer
s.describe() prints A shape because describe() is declared only in a protocol extension, not as a requirement in the Shape protocol body. A method that is not a protocol requirement is statically dispatched, so when it is called through the existential Shape, the compiler resolves the call to the extension's default implementation and never consults Square's method. Only protocol requirements are dispatched dynamically through the protocol witness table. The minimal fix is to declare func describe() -> String inside the Shape protocol body. This makes it a requirement, so s.describe() dispatches dynamically to Square.describe() and prints A square.
The behavior depends on whether a method is a protocol requirement. describe() is defined only in an extension of Shape, so it is not a witness-table entry; a call through the existential Shape is resolved statically to the extension's default implementation, and Square's method is ignored. Declaring func describe() -> String in the protocol body makes it a requirement with a witness-table slot, so the call dispatches dynamically to Square's implementation and prints A square. The fix changes only the protocol declaration, not the conforming type.
What is the exact stdout of this Swift 5.x program? Each print call outputs one line. Give the four output lines in order, separated by newlines.#
protocol P {
func value() -> Int
}
extension P {
func value() -> Int { 0 }
func derived() -> Int { value() * 7 }
}
struct A: P {
func value() -> Int { 3 }
func derived() -> Int { 55 }
}
let p: P = A()
print(p.value())
print(p.derived())
let a = A()
print(a.value())
print(a.derived())Show answer
3
21
3
55
value() is a requirement of the protocol, so p.value() on a P-typed existential is dispatched dynamically through the witness table to A's implementation, printing 3. derived() is not a requirement; it exists only in the protocol extension, so p.derived() is statically dispatched to the extension's version. Inside that version, self is typed P and value() is a requirement, so it dynamically dispatches to A.value() returning 3, and 3 * 7 = 21 is printed. a has the concrete type A, so a.value() calls A.value() (3) and a.derived() calls A's own derived() (55). The four lines are 3, 21, 3, 55.
Consider the following Swift code. Trace the dispatch carefully and determine the exact output printed to the console.#
protocol Shape {Options
Show answer
Shape: area=4.0, perimeter=0.0
The protocol Shape declares area() as a requirement but NOT perimeter() or describe() — those exist only in the protocol extension. When s (of existential type any Shape) calls describe(), the method is not in the witness table, so the compiler dispatches it statically against the static type Shape, selecting the extension's describe(). Inside that extension method, self.area() IS a protocol requirement, so it dispatches through the witness table to Square.area(), returning 2 * 2 = 4.0. However, self.perimeter() is NOT a protocol requirement, so it also dispatches statically to the extension's default perimeter(), returning 0.0. The Square-specific describe() and perimeter() are never reached because the static type is Shape, not Square. This is the core of protocol-oriented dispatch in Swift: only methods declared in the protocol body (requirements) get dynamic witness-table dispatch; extension-only methods are always statically dispatched.
Related interview questions
Job market
See swift salaries and hiring demand from live job postings.
Practise these until they stick
That's every question we hold on this topic, and the page marks what you pick. What it can't do is remember. A free account keeps every answer, and what you miss comes back until it's right: after a day, then at longer gaps.
Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan