Protocol composition is a mechanism in Swift that allows creating a type that must conform to several protocols at the same time. This is an alternative way to replace multiple inheritance, which is not available for classes in Swift.
Background
Objective-C supported multiple inheritance only for protocols, but not for classes. Swift continues this tradition, emphasizing protocols and their combinations to build new abstractions.
Problem
Programmers often face the challenge of creating a type whose behavior is defined by several abstractions. Multiple inheritance inevitably leads to hierarchy conflicts, which is safely resolved in Swift using protocols and protocol composition.
Solution
In Swift, protocols can be combined using the '&' operator. This allows creating variables or function parameters that must conform to multiple protocols at once.
Code example:
protocol Drivable { func drive() } protocol Flyable { func fly() } struct FlyingCar: Drivable, Flyable { func drive() { print("Driving") } func fly() { print("Flying") } } func testVehicle(_ vehicle: Drivable & Flyable) { vehicle.drive() vehicle.fly() } testVehicle(FlyingCar())
Key features:
Can an object that implements only one of the protocols be passed to a parameter with protocol composition?
No, the object must implement all the protocols involved in the composition; otherwise, the compiler will raise an error.
Code example:
// struct Car: Drivable {} — cannot be passed to testVehicle since fly() is not implemented
Does protocol composition work with types, not just with values?
Protocol composition applies to values (variables, function parameters), but is not used when defining the object's type (for example, you cannot declare a new type as some "composition" of protocols — only a variable).
Code example:
var obj: SomeProtocol & AnotherProtocol // acceptable // typealias MyType = SomeClass & AnotherProtocol // error
Can classes and protocols be combined through '&'?
Yes, but with one limitation: only one class type (class or its subclass) can be on the left side, and the rest must be protocols; otherwise, the compiler will raise an error.
Code example:
class A {} protocol B {} // func f(obj: A & B) {} // acceptable // func f(obj: A & AnotherClass & B) {} // error! Only one class type is allowed
In a project where data is passed between layers using objects with protocol composition, even though a single protocol would suffice:
func present(item: Displayable & Serializable) { ... }
Pros:
Using protocol composition only in explicit cases — for example, processing objects that simultaneously support Codable and Identifiable for generalized serialization:
func save<T: Codable & Identifiable>(_ item: T) { ... }
Pros: