Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
21: Extensions in Swift
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
Stringtype. - Add a computed property called
isValidEmailthat returns aBool. For this exercise, a simple check to see if the string contains both "@" and "." is sufficient. - Add a computed property called
isStrongPasswordthat returnstrueif 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: useprefix(1)anddropFirst()).
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"There are no comments for now.