In Swift, the experience of many OOP languages has been generalized and enhanced through the ability to compose (rather than just inherit) protocols. Protocol composition allows you to declare a variable, function parameter, or generic with the requirement to conform to multiple protocols simultaneously. This mechanism is extremely useful when it is necessary to work with objects that exhibit the behavior of multiple contracts (interfaces), while flexibly avoiding the downsides of multiple inheritance. The problem that composition solves is the need to express "an object must satisfy a group of requirements," rather than just one.
Swift uses a special syntax for this: the union of protocols is denoted by the & (ampersand) symbol, for example, protocolA & protocolB. Under the hood, runtime checks are performed (for instance, during type casting and within generic contexts). This minimizes the number of types and flexibly implements the "separation of concerns" pattern.
Example code:
protocol Drawable { func draw() } protocol Movable { func move() } struct Sprite: Drawable, Movable { func draw() { print("Sprite draws") } func move() { print("Sprite moves") } } func animate(object: Drawable & Movable) { object.draw() object.move() } let s = Sprite() animate(object: s)
Key features:
Can you create a variable of type only protocolA & protocolB without tying it to a specific struct or class?
Yes, you can declare a variable as conforming to multiple protocols at once, for example:
var obj: protocolA & protocolB
But it's important: such variables can only reference objects (and not value types), if at least one protocol in the composition is constrained to class types (protocol: AnyObject).
Can a class type be included in the composition, for example, SomeClass & Drawable?
Yes, but with nuances: a composition of the form SomeClass & Protocol requires that the values must be instances of that class (or its subclasses) that implement the protocol. This approach is used to constrain generic types.
Can protocol composition be used as an associated type in protocol extension?
Yes, but there are limitations: you cannot declare an associatedtype as a composition, but you can use where in an extension to constrain protocol compositions, for example, an extension that applies only to types conforming to multiple protocols.
In a project, 5 similar protocols were implemented — Drawable, Movable, Resizable, Colorable, Animatable. Composition was applied everywhere as Drawable & Movable & Resizable & Colorable & Animatable. Typical mistakes were accompanied by complex bugs due to entities failing to implement one of the contracts.
Pros:
Cons:
Instead of a complex composition, two main protocols (for example, Actor and Viewable) were highlighted, a typealias for the composition "DynamicEntity" was created, and it was used everywhere. Responsibilities were clearly delineated.
Pros:
Cons: