Swift Tips & Tricks: Protocol Extensions in Swift
Let's say you want a clamped
function in Swift, where you'd make sure a given number is limited within a range. So that:
20.clamped(min: 1, max: 10) // gives 10
3.clamped(min: 1, max: 10) // gives 3
Extending Int
What first comes to mind is extending Int struct and adding the method there.
extension Int {
func clamped(min min: Int, max: Int) -> Int {
if self < min {
return min
}
if self > max {
return max
}
return self
}
}
Awesome. Now our sample code works.
But what if we try to clamp a double?
(20.3).clamped(min: 1, max: 10) // Error: 'Double' does not have a member named 'clamped'
At this point, you might be thinking of extending Double
type to add the clamped
function there too. But what about UInt
? Or CGFloat
or Int32
? There are a lot of number types in Swift and you can't extend them all...
A Swift Solution
Well, when you inspect our clamped
method, you'll see that we actually only use 2 parameters inside it. Apart from >
and <
operator functions, there is no other functions or methods that we use.
Hmm... Those two look familiar. Inspect the Swift headers and you'll see this:
public protocol Comparable : Equatable {
public func <(lhs: Self, rhs: Self) -> Bool
public func <=(lhs: Self, rhs: Self) -> Bool
public func >=(lhs: Self, rhs: Self) -> Bool
public func >(lhs: Self, rhs: Self) -> Bool
}
Now with Swift 2.0, we can actually extend Comparable
itself! Just replace extension Int
with extension Comparable
and other Int
s with Self
and voila:
extension Comparable {
func clamped(min min: Self, max: Self) -> Self {
if self < min {
return min
}
if self > max {
return max
}
return self
}
}
Clamp All the Things
Now every type that conforms to Comparable can be clamped by things of it's own type.
(20.3).clamped(min: 0.9, max: 10.1) // Gives 10.1
let number: CGFloat = 3.2
number.clamped(min: 0.9, max: 10.1) // Gives 3.2
We can even clamp String
s!
"a".clamped(min: "x", max: "z") // Gives x
"y".clamped(min: "x", max: "z") // Gives y
Being able to extend protocols is a huge update to Swift.