What is protocol composition type in Swift?

Protocol composition type

Protocol composition is the process of combining multiple protocols into a single protocol. You can think of it as multiple inheritance.

With protocol DoSomething, below, class HasSomethingToDo is able to do whatShouldBeDone and anotherThingToBeDone.

// First approach
protocol DoSomething {
func whatShouldBeDone()
func anotherThingToBeDone() -> String
}
// Class conforms to 'DoSomething'
class HasSomethingToDo: DoSomething {
func whatShouldBeDone() {
print("I did Something!")
}
func anotherThingToBeDone() -> String {
print("another thing that I did")
return " "
}
}

What if we have a class that just wants to do whatShouldBeDone or another which needs to do just anotherThingToBeDone? The protocol DoSomething will contain unimplemented requirements for these other classes. An even bigger problem with the approach above is that it violates the Interface Segregation PrincipleThis states that clients should not be forced to use interfaces that they do not need.. We need our requirements to be handled by separate protocols so that the different classes can conform to its respective requirements, as shown below:

// First protocol defined
protocol DoSomething {
func whatShouldBeDone()
}
// Second protocol defined
protocol DoAnotherThing {
func anotherThingToBeDone()
}
// A class conforms to first protocol
class JustDoSomething: DoSomething {
func whatShouldBeDone() {
print("I did Something!")
}
}
// Another class conforms to the second protocol
class JustDoAnotherThing: DoAnotherThing {
func anotherThingToBeDone() {
print("I do something else thing!")
}
}
let classInstance = JustDoSomething()
classInstance.whatShouldBeDone() // I did Something!
//classInstance.anotherThingToBeDone() // will not exist
let secondClassInstance = JustDoAnotherThing()
secondClassInstance.anotherThingToBeDone() // I do something else thing!
//secondClassInstance.anotherThingToBeDone() // will not exist

The basic approach

Now, what happens to our initial class HasSomethingToDo, which actually requires the two abilities?

This is where we introduce the protocol composition type concept. In the basic approach, a generic object-oriented programming methodology, we create an empty protocol that inherits from the other two protocols, thus making our class conform to this new protocol.

// first protocol defined
protocol DoSomething {
func whatShouldBeDone()
}
//second protocol defined
protocol DoAnotherThing {
func anotherThingToBeDone()
}
// The new protocol which inherits from the two protocols from above.
protocol DoTheseTwoThings: DoSomething & DoAnotherThing {
}
// The class conforms to the new protocol.
class HasSomethingToDo: DoTheseTwoThings {
func whatShouldBeDone() {
print("I did Something!")
}
func anotherThingToBeDone() {
print("Another thing to be done!")
}
}
let newInstance = HasSomethingToDo()
newInstance.whatShouldBeDone()
newInstance.anotherThingToBeDone()

The Swifty approach

Swift provides a more “Swifty” way to achieve protocol composition. We are able to combine two or more protocols by using the & operator.
If you observe the earlier examples, we defined two protocols, DoSomething and DoAnotherThing, and those two protocols are now adopted by class HasSomethingToDo. We combine these multiple protocols into a single instance and call it specificActions using protocol composition.

// first protocol defined
protocol DoSomething {
func whatShouldBeDone()
}
//second protocol defined
protocol DoAnotherThing {
func anotherThingToBeDone()
}
class HasSomethingToDo: DoSomething, DoAnotherThing {
func whatShouldBeDone() {
print("I did Something!")
}
func anotherThingToBeDone() {
print("Another thing that I did!")
}
}
let specificActions: DoSomething & DoAnotherThing = HasSomethingToDo()
// we can now access
specificActions.whatShouldBeDone()
specificActions.anotherThingToBeDone()

I suggest using the “Swifty” approach, because we can avoid creating a new empty protocol, only to combine different protocols and use a typealias to clean the concatenation of protocols when it gets a little messy.

There is also opaque type, which differs from protocol type in that it refers to a particular concrete type, whereas protocols can refer to as many types as can conform to it. So opaque type preserves type identity, while protocol type does not, and it’s more flexible about its return type.

Free Resources