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.

Overview

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:

Extensions for Customizing Existing Functionality

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.

Extensions for Adding New Functionality

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"

Extensions for Organizing Assets

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

Extensions for Organizing Structure

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.

Extensions for Custom Inits

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.