Conditional conformance is a feature that lets you provide conformance or extensions to existing types on the condition that a set of requirements is fulfilled.
Let's define a simple protocol:
protocol Summable {
var sum: Int { get }
}
The previous protocol defines that any type that is Summable should expose a sum property of the Int type.
We can retrofit this type on Arrays of Int types as we know how to add integers together to produce a sum:
extension Array: Summable where Element == Int {
var sum: Int {
return self.reduce(0) { $0 + $1 }
}
}
[1,2,3,4].sum == 10
It's all good, but now we also want to make sums of all number types. In Swift, there is a base protocol, called Numeric, that is shared by all number types.
Let's express Summable in terms of Numeric:
protocol Summable {
associatedtype SumType
var sum: SumType { get }
}
Now we define that the sum type will be defined a posteriori at the time a concrete object will declare its conformance to the protocol. We'll dive into more details in the associated types later on:
extension Array: Summable where Element: Numeric {
typealias SumType = Element
var sum: Element {
return self.reduce(0) { $0 + $1 }
}
}
Let's break it down:
- extension Array: Summable declares that we're extending Array to be Summable
- where Element: Numeric is only for arrays that have their elements of the Numeric type.
- typealias SumType = Element declares that the sum will be the type of the array Element type, not Numeric.
Now we can use the following:
let intSum = [1,2,3,4,5].sum
let doubleSum = [1.0, 2.0, 3.0, 4.0].sum
let floatSum: Float = [1.0, 2.0, 3.0, 4.0].sum
assert(intSum is Int)
assert(doubleSum is Double)
assert(floatSum is Float)
The previous code compiles and is valid; we've successfully extended arrays of the Numeric types in order to implement the Summable protocol.
Now, let's say you want to use the same Summable protocol in order to implement it on String arrays:
extension Array: Summable where Element == String {
var sum: String {
return self.reduce("") { $0 + $1 }
}
}
While the previous code seems reasonable, it doesn't compile and fails with the following error:
conflicting conformance of 'Array<Element>' to protocol 'Summable'; there cannot be more than one conformance, even with different conditional bounds
As you may have guessed, we cannot declare multiple conformances with different restrictions; if we want to use the sum computed property to concatenate strings, we need to drop the Summable conformance:
extension Array where Element == String {
var sum: String {
return self.reduce("") { $0 + $1 }
}
}
["Hello", " ", "World", "!"].sum == "Hello World!"
We just had a peek at protocols with associated types with the Summable protocol and we'll go deeper into this feature in the next section.