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.
extension Date {
public var isToday: Bool { ... }
}
extension Date {
public var isToday: Bool { ... }
}
Date().isToday
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!
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 rx
, kf
, or 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
public struct ExtensionsWrapper<Base> {
// 1
public var base: Base
// 2
public init(_ base: Base) {
self.base = base
}
}
public protocol ExtensionsCompatible {
// 1
associatedtype CompatibleType
// 2
static var ext: ExtensionsWrapper<CompatibleType>.Type { get set }
// 3
var ext: ExtensionsWrapper<CompatibleType> { get set }
}
extension ExtensionsCompatible {
public var ext: ExtensionsWrapper<Self> {
get {
return ExtensionsWrapper(self)
}
set {
// 4
}
}
public static var ext: ExtensionsWrapper<Self>.Type {
get {
return ExtensionsWrapper<Self>.self
}
set {
// 4
}
}
}
ExtensionsWrapper
to mutate base typeLet'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
// 1
extension UILabel: ExtensionsCompatible {}
// 2
extension ExtensionsWrapper where Base: UILabel {
public var trimmedText: String? {
let aText = base.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
if aText.isEmpty { return nil }
return aText
}
}
UILabel
conform to ExtensionsCompatible
, this will add the ext
extension to All UILabel
types.UILabel
directly extend ExtensionsWrapper
explicitly when the Base
is UILabel
.let label = UILabel()
label.text = " hello world! \n\n"
print(label.text) // prints " hello world! \n\n"
print(label.ext.trimmedText) // prints "hello world!"
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!
A simple Swift extension to initialize UIColor from a hex string taking into consideration edge cases and hex representation variations
Some extensions to make working with Codable easier!