Skip to Content
Course content

21: Extensions in Swift

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I often see developers treat extensions as nothing more than a "folder" for their code—a way to move a few methods out of a massive class file just to make the file shorter. They think extensions are only for organizing their own custom types. If that's how you're looking at them, you're missing out on one of Swift's most powerful features.

Extensions aren't just for your own types

The real magic of extensions is that they let you add functionality to any type, regardless of who wrote it. You can add methods to Int, String, Array, or even classes inside the iOS SDK that Apple wrote ten years ago. You don't need the original source code; you just need the type.

For example, I've always found it annoying that Swift doesn't have a built-in way to trim whitespace and newlines from a string in a single, readable call. Instead of writing a helper function and passing the string into it every time, I just extend String:

extension String {
    func trimmed() -> String {
        return self.trimmingCharacters(in: .whitespacesAndNewlines)
    }
}

let messyInput = "  hello world  \n"
print(messyInput.trimmed()) // "hello world"

Now, trimmed() feels like it was always part of the language. It's available on every single string in your project. This is much cleaner than creating a StringUtils class and calling StringUtils.trim(myString).

The "No Stored Properties" Rule

Here is where people usually hit a wall: you cannot add a stored property to an extension. I can't stress this enough because it's a frequent source of compiler errors for beginners. You can't suddenly decide that every Int in your app should now have a category variable stored inside it.

However, you can add computed properties. A computed property doesn't store a value; it calculates one on the fly. This is usually exactly what you need anyway.

extension Double {
    var asPercentage: String {
        return "\(Int(self * 100))%"
    }
}

let score = 0.85
print(score.asPercentage) // "85%"

In the example above, we didn't add any new memory storage to the Double type; we just added a convenient way to view a Double as a string. If you try to add var percentageValue: Double = 0.0 inside that extension, Swift will yell at you.

Cleaning up declarations with Protocol Extensions

One of my favorite professional patterns is using extensions to handle protocol conformance. If you have a class that conforms to five different protocols, your main class declaration becomes a cluttered mess of commas.

Instead of doing this:

class UserProfile: Codable, Hashable, Equatable, CustomStringConvertible { 
    // ... all the implementation logic for 4 protocols mixed with business logic
}

I prefer to keep the main declaration lean and use extensions to isolate the "requirements" of each protocol. It makes the code significantly easier to navigate:

class UserProfile {
    var username: String
    // Core business logic here
}

extension UserProfile: Codable {
    // Just the encoding/decoding logic
}

extension UserProfile: CustomStringConvertible {
    var description: String {
        return "User: \(username)"
    }
}

It separates the what the class is from how it behaves in different contexts. I highly recommend this approach for any project that grows beyond a few files.




📋 Practical Task

Implementing a Validation Suite for String

In a real app, you spend a huge amount of time validating user input (emails, passwords, usernames). Instead of scattering if/else logic throughout your ViewControllers or ViewModels, you're going to build a validation suite directly into the String type.

Your Task:

  • Create an extension for the String type.
  • Add a computed property called isValidEmail that returns a Bool. For this exercise, a simple check to see if the string contains both "@" and "." is sufficient.
  • Add a computed property called isStrongPassword that returns true if the string is at least 8 characters long.
  • Add a method called capitalizedFirstLetter() that returns a new string with only the first character capitalized and the rest lowercase (Hint: use prefix(1) and dropFirst()).

Test your code with these cases:

let email = "test@example.com"
print(email.isValidEmail) // Should be true

let pass = "12345"
print(pass.isStrongPassword) // Should be false

let name = "jOHN"
print(name.capitalizedFirstLetter()) // Should be "John"
Rating
0 0

There are no comments for now.

to be the first to leave a comment.