tezvyn:

Protocol-Oriented Programming in Swift

AI-drafted, machine-checkedintermediate

Protocol-oriented programming swaps inheritance for composable capabilities via protocol extensions. Use it to share behavior across structs without forcing a superclass. The footgun is protocol bloat: indirection without reuse is bureaucracy.

WHY IT EXISTS: Swift structs and enums are value types, which means they cannot inherit from a base class or share behavior through subclassing. Early Swift code often shoehorned everything into classes just to reuse code, accepting reference semantics and the risk of shared mutable state. Protocol-oriented programming exists to break that dependency by giving value types a mechanism to share implementations without inheritance, preserving safety and performance while still enabling polymorphism.

THE MENTAL MODEL: Stop drawing family trees and start snapping on capabilities. In protocol-oriented programming, a type is defined by what it can do, not who its parent is. You write a protocol to declare an interface, then use an extension to attach a default implementation to that protocol. Any conforming type picks up the behavior automatically, like adding a module to a spaceship rather than being born into a royal bloodline.

HOW IT WORKS: You declare a protocol with requirements such as methods or properties. Then you write an extension on the protocol itself and provide default implementations for those requirements. A struct, enum, or class that conforms to the protocol receives the defaults for free, but can override any of them. Because Swift protocols support associated types and Self requirements, the compiler can resolve these statically in many cases, avoiding the runtime overhead of dynamic dispatch that often comes with class inheritance.

WHEN TO USE IT: Reach for protocol-oriented programming when you need to share code across multiple types that should remain value types, or when you want to model cross-cutting concerns like serialization, equatability, or network request construction. It is also the standard way to write testable code in Swift. Define a protocol for a network client, inject a mock conforming type in tests, and use the real implementation in production without changing your call sites.

WHEN NOT TO USE IT: Do not reach for a protocol when only one concrete type will ever implement it. A protocol with a single conforming type is usually premature abstraction that adds cognitive overhead and slows compile times. Also avoid protocol-oriented programming when you genuinely need reference identity, shared mutable state, or Objective-C interoperability that relies on dynamic runtime behavior, because structs and static dispatch cannot satisfy those requirements.

ONE CANONICAL EXAMPLE: Imagine you are building a graphics renderer. You define a Drawable protocol with a draw method, and you write a protocol extension that provides a default implementation using a shared rendering pipeline. A struct Circle, a struct Rectangle, and even a class TextLabel can all conform to Drawable. They each inherit nothing from one another, yet they all gain the default draw logic. You can store them in a homogeneous array of Drawable existentials and call draw on each element, achieving polymorphism while keeping the shapes as lightweight value types wherever possible.

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.