Protocol Oriented Extensions
Extensions are without any doubt one of Swift’s awesome features, they can be used to add new functionality to existing classes, structures, enumeration or even protocol types.
When I first started learning Swift I was fascinated by how powerful this can be, infect back then I created SwifterSwift an open source library for extensions that expanded quickly to have more than 500 extensions!
The more extensions we started to accept from contributors, the more conflicts we started to face, well, chances are if you have a nice extension, someone else has also thought about it as well and added it to their library!
To explain the problem in code; let’s assume we have two modules A, and B, B is the one we’re developing and A is a popular framework that everyone uses.
We’re super excited about this new extension that tells if a
Date object is within today or not, so we’re adding it as a public variable in an extension in our framework.
Module A (the popular framework)
Module B (our new framework)
Using the extension from module B
The above code will yield the error
Ambiguous use of ‘isToday’ with A, that says both modules have the same extension (or at least 2 extensions with the same name), and the Swift compiler is confused which one to use 🤔
This is quite a situation here 😅! We can’t ask people not to use the module A, I mean common, it is very popular and in use everywhere. at the same time, we want people to start using our extension in their projects.
Wouldn’t it be great if we can have our extension grouped in one place and not conflicting with others?
Well, this is exactly what we're going to discuss here!
Enter Protocol Extensions
The idea is simple, instead of extending types, create a wrapper struct with a base generic type, and extend your wrapper when the base is the type you want to extend
If you’ve used RxCocoa, Kingfisher, or SnapKit you’ll probably understand what I mean here, it is that
snp extension that you use to access library's extensions so you don’t end up having conflicts with extensions from other libraries.
Let's talk some code shall we
The Wrapper struct
- The base object to extend
- An initializer to creates extensions with the base object.
The Compatible protocol
- The type of base object to extend
- A static namespace holder for our extensions compatible types.
- A namespace holder for our extensions compatible types.
- An empty setter will enable using
ExtensionsWrapperto mutate base type
Let's say we have this cool extension for
UILabel that returns the trimmed text only if it has 1 or more non-whitespace or newline character in it
ExtensionsCompatible, this will add the
extextension to All
- Instead of extending
ExtensionsWrapperexplicitly when the
Of course, there are some cases that require types to be extended directly, however, my suggestion is to consider creating a wrapper type to hold your extensions before publishing your next project.
Not only this will minimize the number of conflicts with other extensions, but will offer a starting point to extend almost every type by conforming to your compatible protocol and extending the wrapper struct!
That's it for now. If you have any suggestions or feedback, please let me know via Twitter!