0
0
Swiftprogramming~15 mins

Protocol composition in practice in Swift - Deep Dive

Choose your learning style9 modes available
Overview - Protocol composition in practice
What is it?
Protocol composition in Swift means combining multiple protocols into one requirement. It lets you say a value must follow several protocols at once, without creating a new protocol. This helps write flexible and reusable code by mixing behaviors easily.
Why it matters
Without protocol composition, you would need to create many new protocols for every combination of behaviors, which becomes hard to manage. Protocol composition solves this by letting you combine existing protocols on the fly, making your code simpler and more adaptable to change.
Where it fits
Before learning protocol composition, you should understand basic protocols and how to conform types to them. After this, you can explore advanced protocol features like associated types and protocol extensions to write even more powerful Swift code.
Mental Model
Core Idea
Protocol composition is like saying 'I want something that behaves like this AND that' without making a new name for it.
Think of it like...
Imagine you want a friend who can both cook and play guitar. Instead of finding a special 'cook-and-guitarist' friend, you just say you want someone who can cook and can play guitar. Protocol composition is like that: combining qualities without inventing a new label.
┌───────────────┐   ┌───────────────┐
│  Protocol A   │   │  Protocol B   │
└──────┬────────┘   └──────┬────────┘
       │                   │
       └─────┬─────────────┘
             │
  ┌──────────▼──────────┐
  │ Protocol A & B Type │
  └─────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding basic protocols
🤔
Concept: Learn what a protocol is and how types follow protocols.
A protocol defines a set of methods or properties that a type must have. For example, a protocol 'Drivable' might require a 'drive()' method. A class or struct that says it conforms to 'Drivable' must implement 'drive()'.
Result
You can create types that promise to have certain behaviors, making your code more organized.
Understanding protocols is key because protocol composition builds on combining these behavior promises.
2
FoundationConforming types to multiple protocols
🤔
Concept: Types can follow more than one protocol at the same time.
A struct can conform to multiple protocols by listing them separated by commas. For example, struct Car: Drivable, Refuelable means Car must implement both protocols' requirements.
Result
Types can have multiple behaviors, making them more versatile.
Knowing that types can conform to many protocols sets the stage for combining protocols in requirements.
3
IntermediateUsing protocol composition in function parameters
🤔Before reading on: do you think you must create a new protocol to require multiple behaviors in a function parameter, or can you combine existing ones directly? Commit to your answer.
Concept: You can require a parameter to conform to multiple protocols without making a new protocol by composing them.
func performTask(item: Drivable & Refuelable) { item.drive(); item.refuel() } means 'item' must follow both protocols. This avoids creating a new protocol just for this combination.
Result
Functions become more flexible and concise by accepting any type that meets all required behaviors.
Understanding this prevents unnecessary protocol proliferation and keeps code simpler.
4
IntermediateCombining protocols with properties and methods
🤔Before reading on: do you think protocol composition only works with methods, or can it combine properties too? Commit to your answer.
Concept: Protocol composition works with any protocol requirements, including properties and methods.
If Protocol A requires a property 'name' and Protocol B requires a method 'start()', composing them means the type must have both 'name' and 'start()'.
Result
You can combine any kind of requirements, making protocol composition very powerful.
Knowing this helps you design protocols that mix data and behavior cleanly.
5
AdvancedUsing protocol composition with generics
🤔Before reading on: do you think protocol composition can be used as a generic constraint, or only in concrete types? Commit to your answer.
Concept: Protocol composition can constrain generic types to require multiple protocols at once.
func process(vehicle: T) { vehicle.drive(); vehicle.refuel() } means 'T' must conform to both protocols. This lets generics be more precise about required behaviors.
Result
Generics become more expressive and safe by combining protocol requirements.
Understanding this unlocks writing reusable, flexible generic code that adapts to multiple behaviors.
6
AdvancedProtocol composition with class-only protocols
🤔
Concept: You can combine protocols that only classes can follow using protocol composition.
If you have protocols marked with 'AnyObject' to restrict to classes, composing them still works. For example, 'Drivable & AnyObject & Refuelable' means the type must be a class and conform to both protocols.
Result
You can enforce class-only behavior while combining multiple protocols.
Knowing this helps when you need reference semantics combined with multiple behaviors.
7
ExpertRuntime type checking with protocol composition
🤔Before reading on: do you think you can check at runtime if a value conforms to multiple protocols combined, or only one at a time? Commit to your answer.
Concept: You can check if a value conforms to a protocol composition at runtime using 'is' or 'as?'.
if let item = someValue as? Drivable & Refuelable { item.drive(); item.refuel() } safely checks both protocols together.
Result
You can write dynamic code that adapts based on multiple behaviors a value supports.
Understanding runtime checks with protocol composition helps avoid crashes and write safer, flexible code.
Under the Hood
Swift treats protocol composition as a special type that requires all combined protocols. At compile time, it ensures the type conforms to each protocol. At runtime, the value carries metadata about all protocols it supports, enabling combined checks and method dispatch.
Why designed this way?
Protocol composition was designed to avoid creating many small protocols for every combination. It leverages Swift's powerful type system and runtime metadata to combine behaviors flexibly without extra boilerplate.
┌─────────────────────────────┐
│       Protocol Composition  │
│  ┌───────────────┐          │
│  │ Protocol A    │          │
│  └───────────────┘          │
│  ┌───────────────┐          │
│  │ Protocol B    │          │
│  └───────────────┘          │
│             │               │
│             ▼               │
│  ┌─────────────────────┐    │
│  │ Combined Type Info  │◄───┤
│  └─────────────────────┘    │
│             │               │
│      Runtime Checks        │
└─────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does protocol composition create a new protocol type you can reuse everywhere? Commit yes or no.
Common Belief:Protocol composition creates a new protocol that you can name and reuse like any other protocol.
Tap to reveal reality
Reality:Protocol composition is an anonymous combination used inline; it does not create a new named protocol.
Why it matters:Thinking it creates a new protocol leads to confusion and misuse, like trying to extend or conform to a composition directly.
Quick: Can a protocol composition be used as a concrete type for variables? Commit yes or no.
Common Belief:You can declare variables with protocol composition types directly and store any conforming value.
Tap to reveal reality
Reality:Protocol composition types can be used as variable types, but only if all combined protocols are class-bound or the value supports all requirements.
Why it matters:Misunderstanding this causes compiler errors or runtime crashes when storing incompatible types.
Quick: Does protocol composition affect the inheritance hierarchy of types? Commit yes or no.
Common Belief:Combining protocols changes the class inheritance or creates new inheritance relationships.
Tap to reveal reality
Reality:Protocol composition only combines protocols; it does not affect class inheritance or create new classes.
Why it matters:Confusing protocol composition with inheritance leads to wrong assumptions about method overriding and object layout.
Quick: Can you use protocol composition with protocols that have associated types? Commit yes or no.
Common Belief:You can freely compose any protocols, including those with associated types, using protocol composition.
Tap to reveal reality
Reality:Protocols with associated types cannot be composed directly without specifying the associated types, limiting composition use.
Why it matters:Trying to compose such protocols causes compiler errors and blocks flexible code design.
Expert Zone
1
Protocol composition types are not nominal types, so you cannot extend or add conformance to them directly.
2
Using protocol composition with class-only protocols allows leveraging reference semantics combined with multiple behaviors.
3
Runtime casting with protocol composition can be optimized by the compiler to avoid redundant checks.
When NOT to use
Avoid protocol composition when you need to reuse the combined protocol type in many places or extend it; instead, define a new protocol that inherits the others. Also, do not use it with protocols that have unresolved associated types.
Production Patterns
In production, protocol composition is often used in function parameters and generic constraints to keep APIs flexible. It helps avoid protocol explosion and keeps code modular by combining behaviors on demand.
Connections
Multiple inheritance (OOP)
Protocol composition is similar to multiple inheritance but safer and more flexible.
Understanding protocol composition clarifies how Swift avoids the complexity of multiple inheritance by combining behaviors without sharing implementation.
Type classes (Haskell)
Protocol composition resembles combining type classes to require multiple constraints.
Knowing this connection helps appreciate how different languages solve the problem of combining behaviors in types.
Set intersection (Mathematics)
Protocol composition acts like the intersection of sets, requiring membership in all combined sets.
This mathematical view helps understand why a value must satisfy all protocols simultaneously.
Common Pitfalls
#1Trying to create a new named protocol by composing protocols inline.
Wrong approach:protocol Combined: Drivable & Refuelable {}
Correct approach:protocol Combined: Drivable, Refuelable {}
Root cause:Misunderstanding that '&' is only for inline composition, not for protocol inheritance declarations.
#2Using protocol composition with protocols that have associated types without specifying them.
Wrong approach:func doSomething(item: Sequence & Collection) { }
Correct approach:func doSomething(item: S) where S.Element == Int { }
Root cause:Not realizing protocols with associated types require generics or type constraints to be used properly.
#3Assuming protocol composition changes class inheritance or allows method overriding across protocols.
Wrong approach:class MyClass: BaseClass, Drivable & Refuelable { }
Correct approach:class MyClass: BaseClass, Drivable, Refuelable { }
Root cause:Confusing protocol composition syntax with class inheritance syntax.
Key Takeaways
Protocol composition lets you combine multiple protocols inline without creating new protocol names.
It enables flexible and reusable code by requiring multiple behaviors simultaneously.
You can use protocol composition in function parameters, generics, and runtime checks.
It works with any protocol requirements, including properties and methods, but has limits with associated types.
Understanding protocol composition helps avoid unnecessary protocol proliferation and keeps Swift code clean and adaptable.