Protocol extensions were introduced in Swift to support the ideology of protocol-oriented programming: a programmer can implement default method implementations directly at the protocol level rather than through a base class ancestor or global functions. This reduces code duplication and allows for flexible adaptation of type behavior.
The problem arises when a default implementation masks the need for a custom (override), or when the line of responsibility becomes blurred — especially if a type implements multiple protocols with overlaps. Additionally, it's important to remember: if a method is implemented in the type itself, it will always override the implementation from the extension.
The solution is to use protocol extensions only for universal behavior, and in specific cases explicitly implement the method within the type. It's advisable to avoid overloading the same methods in two extensions for the same protocol.
Example code:
protocol Flyer { func fly() } extension Flyer { func fly() { print("Default flying") } } struct Bird: Flyer {} let sparrow = Bird() sparrow.fly() // Will output: Default flying
Key features:
Can a protocol extension add a stored property to a protocol?
No, only computed properties and methods can be added in protocol extensions. Stored properties are not allowed.
What will happen if a protocol and a type have different implementations of a method with the same name? Which one will be called?
When directly calling the type, the type's implementation will be called; when accessing an instance through a protocol type reference, the protocol extension implementation will be called.
protocol Greeter { func greet() } extension Greeter { func greet() { print("Hi from extension") } } struct Person: Greeter { func greet() { print("Hi from type") } } let person = Person() person.greet() // Hi from type let greeter: Greeter = person greeter.greet() // Hi from extension
Can you use where constraints in protocol extensions for restrictions?
Yes. This is one of the powerful capabilities — a protocol can be extended only for specific types.
extension Collection where Element: Equatable { func allEqual() -> Bool { guard let first = self.first else { return true } return allSatisfy { $0 == first } } }
**Negative case
The team decided to implement error logging through a protocol extension, not considering that each service might want to add its specific format. As a result, different services call the function through protocol references, and the behavior logic differs from expectations.
Pros:
Cons:
**Positive case
Protocols are extended only for those cases where behavior is always universal. For special cases — explicit implementation of methods in the type, and there is a code review on conflict areas.
Pros:
Cons: