tezvyn:

Protocols: Swift's Blueprint for Behavior

AI-drafted, machine-checkedSource: docs.swift.orgadvanced

Protocols are Swift's blueprints for behavior, enabling composition over inheritance. They're used to decouple dependencies and define shared functionality like `Codable`. The footgun is mistaking `any Protocol` for `some Protocol`, inviting performance costs.

WHY IT EXISTS: Swift protocols were designed to solve the limitations of single-inheritance class hierarchies. In traditional object-oriented programming, if you want to share behavior, you often have to create a common base class. This can lead to bloated, inflexible superclasses and deep, tangled inheritance chains. Protocols offer a more flexible alternative through composition, allowing a type to adopt multiple, independent sets of behaviors.

THE MENTAL MODEL: A protocol is a contract or a blueprint. It defines a set of required properties, methods, and other capabilities without providing an implementation. Any struct, class, or enum can "conform" to a protocol, promising to fulfill that contract. This allows you to write code that operates on a capability (the protocol) rather than a specific concrete type. This is the foundation of Protocol-Oriented Programming (POP), a paradigm that favors composing behaviors over inheriting from base classes.

HOW IT WORKS: A protocol is declared with the protocol keyword, followed by a list of requirements. These can include properties, methods, initializers, and associated types. When a type conforms, it must provide a concrete implementation for all requirements. The Swift compiler enforces this. Advanced usage involves opaque types (some Protocol) and existential types (any Protocol). some Protocol promises a specific, consistent concrete type, hiding its identity. any Protocol creates an "existential container" that can hold an instance of any conforming type at runtime, offering flexibility at the cost of performance due to dynamic dispatch and potential heap allocation.

WHEN TO USE IT: Protocols are ideal for several scenarios. First, for defining shared functionality across unrelated types, like making different data models Codable or Equatable. Second, for creating abstractions for dependency injection, where you code against a protocol and can inject a real service or a mock object for testing. Third, for building generic functions and algorithms that can operate on any type conforming to a specific protocol, such as a function that works on any Sequence.

WHEN NOT TO USE IT: If you need shared stored properties and a default implementation that all subtypes should inherit, a class hierarchy might be simpler. Be cautious of creating a "protocol explosion," where every small feature gets its own protocol, making the codebase hard to navigate. The performance overhead of existential types (any Protocol) means they should be used judiciously, especially in performance-critical code paths. Prefer some Protocol or generics where possible.

ONE CANONICAL EXAMPLE: The Codable protocol is a perfect example of POP. It's a type alias for the Encodable and Decodable protocols. By conforming a custom User struct to Codable, you can easily serialize it to and from formats like JSON. This behavior is composed onto your type, rather than inherited from a base class, allowing it to remain a lightweight value type while gaining powerful serialization capabilities.

Read the original → docs.swift.org

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.