Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Extensions in Swift: How and when to use them

Extensions in Swift allow you to extend an existing class, struct, enumeration, or protocol with new functionality. Whether it’s a custom type defined by you or a current type inside of a framework, extensions can create custom accessors and enhance the types you work with.

So-called retroactive modeling allows you to extend types for which you don’t have access to the original source code. This powerful feature of Swift enables you to create custom layers on top of external code. When and how to use them is an important skill to own as an app developer, so let’s dive in.

What is an extension?

Quite literally, an extension extends existing code. For example, you might have a Person struct with a first and last name:

struct Person {
    let firstName: String
    let lastName: String
}

You can create an extension for this structure and add a fullName property:

extension Person {
    var fullName: String {
        firstName + " " + lastName
    }
}

In this case, we have access to the source code of the Person structure, so we could also decide to define the fullName computed property inside the struct itself:

struct Person {
    let firstName: String
    let lastName: String

    var fullName: String {
        firstName + " " + lastName
    }
}

This makes more sense for this example as it keeps all the available properties in a single place.

Extending types without having access to the source code

When you’re unable to customize the implementation of a type due to lacking source code access, you can use extensions to create custom accessors. For example, we could create a Date extension to add a days to the current value:

extension Date {
    func addingDays(_ days: Int) -> Date {
        Calendar.current.date(byAdding: .day, value: days, to: self)!
    }
}

We’ve now extended the Date type by adding a new method. We can use that method as follows:

Date().addingDays(7)

Even though we can’t access the source code of the Date type, we have been able to extend it with custom functionality. This can be a great way to create discoverable code, since any extension method will be findable using autocompletion:

Extensions in Swift allow you to extend types, even when you don't have source code access.
Extensions in Swift allow you to extend types, even when you don’t have source code access.

By making your custom code more discoverable, you allow others in your team to discover custom (often helpful) extensions. This works better than creating a custom type (E.g., DateDaysCalculator) that everyone should be aware of.

Stay updated with the latest in Swift

The 2nd largest newsletter in the Apple development community with 18,598 developers. Don't miss out – Join today:


Using extensions to create default protocol implementations

Protocol interfaces don’t allow you to write optional members, even though some methods might not be required for every instance. Secondly, you might want to provide default behavior and enable types to overwrite this if needed. We can use extensions to create this behavior.

For example, imagine having the following NameContaining protocol:

protocol NameContaining {
    var firstName: String { get }
    var lastName: String { get }
    var fullName: String { get }
}

We could make our Person struct conform to this protocol without any changes:

struct Person: NameContaining {
    let firstName: String
    let lastName: String
    
    var fullName: String {
        firstName + " " + lastName
    }
}

Now imagine we would introduce another NameContaining type, but this time it’s a Pet:

struct Pet: NameContaining {
    let firstName: String
    let lastName: String

    var fullName: String {
        firstName + " " + lastName
    }
}

You can see we have to duplicate the computed fullName property, even though its functionality matches the Person‘s implementation. Using a protocol extension, we can remove boilerplate code while keeping the same functionality:

protocol NameContaining {
    var firstName: String { get }
    var lastName: String { get }
    var fullName: String { get }
}

extension NameContaining {
    /// This computed property provides a default implementation for the `NameContaining` protocol.
    var fullName: String {
        firstName + " " + lastName
    }
}

struct Person: NameContaining {
    let firstName: String
    let lastName: String
}

struct Pet: NameContaining {
    let firstName: String
    let lastName: String
}

This technique is powerful and prevents you from writing many boilerplate code. In case you do want to provide a custom implementation, you can always decide to overwrite the default implementation:

struct Person: NameContaining {
    let firstName: String
    let lastName: String

    var fullName: String {
        let firstNameFirstChar = firstName.first!.uppercased()
        return firstNameFirstChar + ". " + lastName
    }
}

struct Pet: NameContaining {
    let firstName: String
    let lastName: String
}

let person = Person(firstName: "Antoine", lastName: "van der Lee")
let pet = Pet(firstName: "Bernie", lastName: "van der Lee")

/// Prints: "A. van der Lee" using the custom implementation of `fullName`
print(person.fullName)
/// Prints "Bernie van der Lee" using the default implementation of `fullName`
print(pet.fullName)

Conforming to a protocol using an extension

Extensions in Swift also allow you to add protocol conformance. This can be useful if you want to define protocol conformance outside the source file or in cases where you don’t have access to the source code.

For example, we could extend the CNContact type of the ContactsUI framework and make it conform to our NameContaining protocol:

import ContactsUI

extension CNContact: NameContaining {
    var firstName: String {
        givenName
    }
    var lastName: String {
        middleName + familyName
    }
    var fullName: String {
        givenName + middleName + familyName
    }
}

This now enables us to create instances that work with the NameContaining protocol, without knowing whether they’re dealing with a Pet, Person or CNContact:

import SwiftUI

struct NameView: View {
    let nameContaining: NameContaining

    var body: some View {
        Text("This person's name is: \(nameContaining.fullName)")
    }
}

Conclusion

Extensions in Swift are a powerful feature that allows you to write extensions to structs, classes, protocols, or enumerations. Whether you’ve access to the source code or not, extensions can extend any of those types. Using extensions in conjunction with protocols, you can write reusable code without too much boilerplate.

If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.

Thanks!