Swift Extensions
Swift extensions are a unique feature to the Swift language. I will try to outline all the different ways extensions are used in a project.
We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Swift extensions are a unique aspect to the Swift language. The Official Definition mentions that extensions are about “…adding new functionality to existing class, structure, enumeration or protocol types.”
Extensions can be more then just adding functionality. It can be a good way of organizing and formatting your thoughts into code so your future self can have a better understanding of what is going on.
Let’s get into some examples:
In popular web frameworks like Ruby or PHP, they have a function for trimming the whitespace on a string trim()
or strip()
. Swift does have a similar feature it’s called trimmingCharacters(in:) | Apple Developer Documentation
Anytime I want to trim white space around a string I need to remember .trimmingCharacters(in: .whitespacesAndNewlines)
. It’s a long method name to remember.
With extensions I can write an extension on String
and wrap trimmingCharacters(in:)
into something I can remember called trimmed
.
import Foundation
extension String {
var trimmed: String {
return self.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
var example = " some space is here ".trimmed
example.trimmed
print(example) // "some space is here"
I try to make an effort to remember the API that is relevant in swift. But Extensions
do allow you to do this sort of thing. When I do create extensions that are common I would place them in it’s own folder.
I will create a folder called “Extensions” and then add a naming conventions like so. I believe this convention is copied from the days of Objective-C and using categories.
And I believe this convention is for extensions on files that you don’t own. So I don’t own String
, Array
or Color
. Thus we follow this format.
In a language like Elixir I like this function called Enum.intersperse/2.
I would like to add it to Swift. I can do so by extending the Array
class with the following:
extension Array {
func interspersing(_ element: Element) -> Array {
flatMap { [$0, element] }.dropLast()
}
}
Interspersing, is about placing an element between items within an array. An example can be taking an Array of words and placing a comma between the words.
Followed by using reduce
to turn that array into a string.
let final = ["one", "two", "three"].interspersing(", ")
print(final) // ["one,", ", "two", ", ", "three"]
final.reduce("") { (result, item) -> String in
result + item
}
print(final) // "one, two, three"
I’m aware that the Swift library already supports something similar to the above reduce
function. You could have just done the following:
let final = ["one", "two", "three"].joined(separator: ", ")
print(final) // "one, two, three"
If you have a lot of common colors you can use extensions to add those colors by adding an extension to Color
or UIColor
Grouping common colors like this makes it much easier to find the colors you need when your using Xcode and the autocomplete window appears.
import SwiftUI
extension Color {
static let brandColor = Color.red
static let footerColor = Color.blue
static let backgroundColor = Color.blue
}
// You can now access those colors
Color.brandColor
Make the properties static
and you can access them with needing to init()
.
It also works with UIColor
extension UIColor {
static let brandColor = UIColor.red
static let footerColor = UIColor.blue
static let backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
}
// You can now access those colors
UIColor.backgroundColor
You can use extensions to break your structure into smaller pieces so it’s easier to reason. In this example you can take your common UITableViewController and break it into 3 separate parts.
// BEFORE
class ContenTableViewController: UIViewController, UITableViewDelegate, UITableViewDelegate {
}
// AFTER
class ContenTableViewController: UIViewController {}
extension ContenTableViewController: UITableViewDelegate {}
extension ContenTableViewController: UITableViewDataSource {}
I would take 1 file and just reorganize it this way so. You can now see where your tableview specifics are organized vs viewDidLoad methods and properties.
This example doesn’t add any new functionality but it just seperates it out so its easier to read in my opinion. UITableViewController
is a rather large class and seeing it seperated out like this helps.
In this example we are going to have a User struct:
struct User {
var first_name: String
var last_name: String
}
Now if you wanted to initialize this you can do so with the following:
User(first_name: String, last_name: String)
If you wanted to add another way to initialize this struct with a dictionary you can do so like this:
struct User {
var first_name: String
var last_name: String
init(dictionary: [String: String]) {
self.first_name = dictionary["first_name"] ?? ""
self.last_name = dictionary["first_name"] ?? ""
}
}
The problems with this is you now have lost your original init. Now the only way to create this struct is by using a Dictionary.
However, if you move init(dictionary: [String: String])
into an Extension then you can have both ways of initializing your struct.
struct User {
var first_name: String
var last_name: String
}
extension User {
init(dictionary: [String: String]) {
self.first_name = dictionary["first_name"] ?? ""
self.last_name = dictionary["first_name"] ?? ""
}
func asDictionary() -> [String: String] {
return [
"first_name": self.first_name,
"last_name": self.last_name
]
}
}
Now look at autocomplete and you should see both examples.
This example might be too specific but I like the fact that anything that deals with a Dictionary
are grouped within the extension outline. It makes it easy to scan and reason with.
In the future you might support the ability to init a user via JSON or XML or some other means. Grouping them seperate via extensions helps shape the information.
Even though the User
extension is broken into sections I would keep it all in one file.