Generic type extension by retrofitting protocols in Swift 2

A common Swift problem is that extensions of generics can only be constrained to protocols. We use a lot of big words here but this issue is quite common and a simple example will clearly illustrate this situation.
So let’s assume that we want to detect if an optional string is either empty or nil.
The basic solution would look something like this

let myString: String? = …
if myString?.isEmpty ?? true { … }

This looks like a bit bloated for such a simple operation and we would rather use something like

let myString: String? = …
if myString.isEmpty { … }

However this property does not exist and we are immediately thinking of adding an extension to Optional type to help us doing this :

extension Optional where Wrapped: String {
    var isEmpty: Bool {
        return map { $0.isEmpty } ?? true
    }
}

But there we fall short on a Swift limitation :

Type ‘Wrapped’ constrained to non-protocol type ‘String’.

Swift only accepts protocol as constraints on generic type extensions.
This especially does not work on value types (structs, enums, base types).
An interesting thing is that swift implicitely provides a protocol for classes describing their interface. You can therefore constraint to classes easily.
We will now see how to constraint on value types.

Solution

protocol PossiblyEmpty {
    var isEmpty: Bool { get }
}

Now let’s retrofit it on String. We only need to declare it as the implementation already exists :

extension String: PossiblyEmpty {}

Now let’s update our Optional extension to support this protocol:

extension Optional where Wrapped: PossiblyEmpty { … }

and we are done:

let myString: String? = …
if myString.isEmpty { … }

Bonus code: we can even retrofit more implementations as long as they provide an implementation for this signature !

extension Array: PossiblyEmpty {}

Here is the full code, you can add comment, edit or anything you want!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s