Generic Optional Handling in Swift
Posted 13 Aug 2017
Sometimes you want to write a generic algorithm working on any Optional
type no matter what actual type
is wrapped inside. Ok, this can be easily done with a free generic function, but what if you want to write
a Sequence
extension to remove all nil
values for example?
Things get a little bit complicated here since Optional
is not a protocol but a concrete type and so it
can’t be used as a generic type constraint.
Generic protocols and concrete types in Swift serve different purposes: we create instances and declare variables of concrete types while protocols can be used as a generic type constraints.
Type erasure is a technique used in case we want to declare a variable able to hold any concrete type conforming to a specific protocol. What we want here instead is another part of the same puzzle, we need a protocol allowing us to use the concrete generic type as a constraint.
This may sound complicated in English but, as it usually happens, looks much simpler when written in plain Swift 😄
public protocol OptionalType: ExpressibleByNilLiteral {
associatedtype WrappedType
var asOptional: WrappedType? { get }
}
extension Optional: OptionalType {
public var asOptional: Wrapped? {
return self
}
}
So here we just define an OptionalType
protocol declaring WrappedType
associated type and conform Optional
enum to it. Note that generic type parameters do not automatically fulfill protocol requirements, but we
have type inference covering us here.
In order to make the protocol useful we expose some basic Optional
functionality:
-
asOptional
property gives us an access to the optional binding syntax. -
ExpressibleByNilLiteral
conformance allows us to use nil for initialization.
Having done that, we can now use OptionalType
as a generic constraint:
public extension Sequence where Self.Iterator.Element: OptionalType {
public func removingNils() -> [Self.Iterator.Element.WrappedType] {
return flatMap { $0.asOptional }
}
}
If you are looking for more tricks that can be done with the Optional
enum,
you should check an excellent article by Russ Bishop.